Skip to content

Instantly share code, notes, and snippets.

@liads
Created June 16, 2019 20:53
Show Gist options
  • Save liads/c702fd4b8529991af9cd52d03b694814 to your computer and use it in GitHub Desktop.
Save liads/c702fd4b8529991af9cd52d03b694814 to your computer and use it in GitHub Desktop.
ESPHome climate component for Electra AC (RC-3 IR remote)
#include "esphome.h"
static const char *TAG = "electra.climate";
typedef enum IRElectraMode {
IRElectraModeCool = 0b001,
IRElectraModeHeat = 0b010,
IRElectraModeAuto = 0b011,
IRElectraModeDry = 0b100,
IRElectraModeFan = 0b101,
IRElectraModeOff = 0b111
} IRElectraMode;
typedef enum IRElectraFan {
IRElectraFanLow = 0b00,
IRElectraFanMedium = 0b01,
IRElectraFanHigh = 0b10,
IRElectraFanAuto = 0b11
} IRElectraFan;
// That configuration has a total of 34 bits
// 33: Power bit, if this bit is ON, the A/C will toggle it's power.
// 32-30: Mode - Cool, heat etc.
// 29-28: Fan - Low, medium etc.
// 27-26: Zeros
// 25: Swing On/Off
// 24: iFeel On/Off
// 23: Zero
// 22-19: Temperature, where 15 is 0000, 30 is 1111
// 18: Sleep mode On/Off
// 17- 2: Zeros
// 1: One
// 0: Zero
typedef union ElectraCode {
uint64_t num;
struct {
uint64_t zeros1 : 1;
uint64_t ones1 : 1;
uint64_t zeros2 : 16;
uint64_t sleep : 1;
uint64_t temperature : 4;
uint64_t zeros3 : 1;
uint64_t ifeel : 1;
uint64_t swing : 1;
uint64_t zeros4 : 2;
uint64_t fan : 2;
uint64_t mode : 3;
uint64_t power : 1;
};
} ElectraCode;
const uint8_t ELECTRA_TEMP_MIN = 16; // Celsius
const uint8_t ELECTRA_TEMP_MAX = 30; // Celsius
#define ELECTRA_TIME_UNIT 1000
#define ELECTRA_NUM_BITS 34
class ElectraClimate : public climate::Climate, public Component {
public:
void setup() override
{
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 = roundf(this->current_temperature);
}
this->active_mode_ = this->mode;
}
void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
this->transmitter_ = transmitter;
}
void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
/// Override control to change settings of the climate device
void control(const climate::ClimateCall &call) override
{
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();
this->active_mode_ = this->mode;
}
/// Return the traits of this controller
climate::ClimateTraits traits() override
{
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(ELECTRA_TEMP_MIN);
traits.set_visual_max_temperature(ELECTRA_TEMP_MAX);
traits.set_visual_temperature_step(1);
return traits;
}
/// Transmit the state of this climate controller via IR
void transmit_state_()
{
ElectraCode code = { 0 };
code.ones1 = 1;
code.fan = IRElectraFan::IRElectraFanAuto;
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
code.mode = IRElectraMode::IRElectraModeCool;
code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
break;
case climate::CLIMATE_MODE_HEAT:
code.mode = IRElectraMode::IRElectraModeHeat;
code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
break;
case climate::CLIMATE_MODE_AUTO:
code.mode = IRElectraMode::IRElectraModeAuto;
code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
break;
case climate::CLIMATE_MODE_OFF:
default:
code.mode = IRElectraMode::IRElectraModeOff;
break;
}
auto temp = (uint8_t) roundf(clamp(this->target_temperature, ELECTRA_TEMP_MIN, ELECTRA_TEMP_MAX));
code.temperature = temp - 15;
ESP_LOGD(TAG, "Sending electra code: %lld", code.num);
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(38000);
uint16_t repeat = 3;
for (uint16_t r = 0; r < repeat; r++) {
// Header
data->mark(3 * ELECTRA_TIME_UNIT);
uint16_t next_value = 3 * ELECTRA_TIME_UNIT;
bool is_next_space = true;
// Data
for (int j = ELECTRA_NUM_BITS - 1; j>=0; j--)
{
uint8_t bit = (code.num >> j) & 1;
// if current index is SPACE
if (is_next_space) {
// one is one unit low, then one unit up
// since we're pointing at SPACE, we should increase it by a unit
// then add another MARK unit
if (bit == 1) {
data->space(next_value + ELECTRA_TIME_UNIT);
next_value = ELECTRA_TIME_UNIT;
is_next_space = false;
} else {
// we need a MARK unit, then SPACE unit
data->space(next_value);
data->mark(ELECTRA_TIME_UNIT);
next_value = ELECTRA_TIME_UNIT;
is_next_space = true;
}
} else {
// current index is MARK
// one is one unit low, then one unit up
if (bit == 1) {
data->mark(next_value);
data->space(ELECTRA_TIME_UNIT);
next_value = ELECTRA_TIME_UNIT;
is_next_space = false;
} else {
data->mark(next_value + ELECTRA_TIME_UNIT);
next_value = ELECTRA_TIME_UNIT;
is_next_space = true;
}
}
}
// Last value must be SPACE
data->space(next_value);
}
// Footer
data->mark(4 * ELECTRA_TIME_UNIT);
transmit.perform();
}
ClimateMode active_mode_;
bool supports_cool_{true};
bool supports_heat_{true};
remote_transmitter::RemoteTransmitterComponent *transmitter_;
sensor::Sensor *sensor_{nullptr};
};
@liads
Copy link
Author

liads commented Jun 16, 2019

Usage example in ESPHome yaml file:

climate:
  - platform: custom
    lambda: |-
      auto electra_climate = new ElectraClimate();
      electra_climate->set_sensor(id(my_temperature_sensor)); // Optional
      electra_climate->set_transmitter(id(my_ir_transmitter));
      App.register_component(electra_climate);
      return {electra_climate};

    climates:
      - name: "My Electra AC"

@thebne
Copy link

thebne commented Sep 11, 2021

2021 update: this is still working!
don't forget to add sensor: to your yaml file if you don't have that there already.

Thank you @liads

@mbrevda
Copy link

mbrevda commented Nov 14, 2022

Error on compile. Is there an updated version of this? Any way to get it integrated into esphome core?

@Arbel-arad
Copy link

i also get a compile error:
image
image

@Arbel-arad
Copy link

Arbel-arad commented Jan 3, 2023

so i went and fixed the errors:

#include "esphome.h"

static const char *TAG = "electra.climate";

typedef enum IRElectraMode {
    IRElectraModeCool = 0b001,
    IRElectraModeHeat = 0b010,
    IRElectraModeAuto = 0b011,
    IRElectraModeDry  = 0b100,
    IRElectraModeFan  = 0b101,
    IRElectraModeOff  = 0b111
} IRElectraMode;

typedef enum IRElectraFan {
    IRElectraFanLow    = 0b00,
    IRElectraFanMedium = 0b01,
    IRElectraFanHigh   = 0b10,
    IRElectraFanAuto   = 0b11
} IRElectraFan;

// That configuration has a total of 34 bits
//    33: Power bit, if this bit is ON, the A/C will toggle it's power.
// 32-30: Mode - Cool, heat etc.
// 29-28: Fan - Low, medium etc.
// 27-26: Zeros
//    25: Swing On/Off
//    24: iFeel On/Off
//    23: Zero
// 22-19: Temperature, where 15 is 0000, 30 is 1111
//    18: Sleep mode On/Off
// 17- 2: Zeros
//     1: One
//     0: Zero
typedef union ElectraCode {
    uint64_t num;
    struct {
        uint64_t zeros1 : 1;
        uint64_t ones1 : 1;
        uint64_t zeros2 : 16;
        uint64_t sleep : 1;
        uint64_t temperature : 4;
        uint64_t zeros3 : 1;
        uint64_t ifeel : 1;
        uint64_t swing : 1;
        uint64_t zeros4 : 2;
        uint64_t fan : 2;
        uint64_t mode : 3;
        uint64_t power : 1;
    };
} ElectraCode;

const uint8_t ELECTRA_TEMP_MIN = 16;  // Celsius
const uint8_t ELECTRA_TEMP_MAX = 30;  // Celsius
uint8_t target = 21;

#define ELECTRA_TIME_UNIT 1000
#define ELECTRA_NUM_BITS 34

class ElectraClimate : public climate::Climate, public Component {
 public:
  void setup() override
  {
    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 = roundf(this->current_temperature);
    }

    this->active_mode_ = this->mode;
  }

  void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
    this->transmitter_ = transmitter;
  }

  void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
  void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
  void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }

  /// Override control to change settings of the climate device
  void control(const climate::ClimateCall &call) override
  {
    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();

    this->active_mode_ = this->mode;
  }

  /// Return the traits of this controller
  climate::ClimateTraits traits() override
  {
    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(ELECTRA_TEMP_MIN);
    traits.set_visual_max_temperature(ELECTRA_TEMP_MAX);
    traits.set_visual_temperature_step(1);
    return traits;
  }

  /// Transmit the state of this climate controller via IR
  void transmit_state_()
  {
    ElectraCode code = { 0 };
    code.ones1 = 1;
    code.fan = IRElectraFan::IRElectraFanAuto;

    switch (this->mode) {
      case climate::CLIMATE_MODE_COOL:
        code.mode = IRElectraMode::IRElectraModeCool;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_HEAT:
        code.mode = IRElectraMode::IRElectraModeHeat;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_AUTO:
        code.mode = IRElectraMode::IRElectraModeAuto;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_OFF:
      default:
        code.mode = IRElectraMode::IRElectraModeOff;
        break;
    }
    target = (uint8_t) this->target_temperature;
    auto temp = (uint8_t) roundf(clamp(target, ELECTRA_TEMP_MIN, ELECTRA_TEMP_MAX));
    code.temperature = temp - 15;

    ESP_LOGD(TAG, "Sending electra code: %lld", code.num);

    auto transmit = this->transmitter_->transmit();
    auto data = transmit.get_data();

    data->set_carrier_frequency(38000);
    uint16_t repeat = 3;

    for (uint16_t r = 0; r < repeat; r++) {
      // Header
      data->mark(3 * ELECTRA_TIME_UNIT);
      uint16_t next_value = 3 * ELECTRA_TIME_UNIT;
      bool is_next_space = true;

      // Data
      for (int j = ELECTRA_NUM_BITS - 1; j>=0; j--)
      {
        uint8_t bit = (code.num >> j) & 1;

        // if current index is SPACE
        if (is_next_space) {
          // one is one unit low, then one unit up
          // since we're pointing at SPACE, we should increase it by a unit
          // then add another MARK unit
          if (bit == 1) {
            data->space(next_value + ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = false;

          } else {
            // we need a MARK unit, then SPACE unit
            data->space(next_value);
            data->mark(ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = true;
          }

        } else {
          // current index is MARK
          
          // one is one unit low, then one unit up
          if (bit == 1) {
            data->mark(next_value);
            data->space(ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = false;

          } else {
            data->mark(next_value + ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = true;
          }
        }
      }

      // Last value must be SPACE
      data->space(next_value);
    }

    // Footer
    data->mark(4 * ELECTRA_TIME_UNIT);

    transmit.perform();
  }

  ClimateMode active_mode_;

  bool supports_cool_{true};
  bool supports_heat_{true};

  remote_transmitter::RemoteTransmitterComponent *transmitter_;
  sensor::Sensor *sensor_{nullptr};
};

@mbrevda @

@schlumm
Copy link

schlumm commented Aug 6, 2023

I've tried to compile it but got the error message that
src\ElectraClimate.h: In member function 'virtual esphome::climate::ClimateTraits ElectraClimate::traits()':
src\ElectraClimate.h:116:39: warning: 'void esphome::climate::ClimateTraits::set_supports_auto_mode(bool)' is deprecated: This method is deprecated, use set_supported_modes() instead [-Wdeprecated-declarations]
116 | traits.set_supports_auto_mode(true);

this error is for all the modes

the solution is to change the climate:

climate::ClimateTraits traits() override
{
  auto traits = climate::ClimateTraits();
  traits.set_supports_current_temperature(this->sensor_ != nullptr);
  traits.set_supported_modes({ climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_OFF });
  return traits;
}

@schlumm
Copy link

schlumm commented Aug 8, 2023

Can anyone knows how to add the support for the IR receiver, so a change with the regular remote will be reflected in HA?

@schlumm
Copy link

schlumm commented Aug 20, 2023

In order to add the support to the fan mode as well, need to have this file:

#include "esphome.h"

static const char *TAG = "electra.climate";

typedef enum IRElectraMode {
    IRElectraModeCool = 0b001,
    IRElectraModeHeat = 0b010,
    IRElectraModeAuto = 0b011,
    IRElectraModeDry  = 0b100,
    IRElectraModeFan  = 0b101,
    IRElectraModeOff  = 0b111
} IRElectraMode;

typedef enum IRElectraFan {
    IRElectraFanLow    = 0b00,
    IRElectraFanMedium = 0b01,
    IRElectraFanHigh   = 0b10,
    IRElectraFanAuto   = 0b11
} IRElectraFan;

// That configuration has a total of 34 bits
//    33: Power bit, if this bit is ON, the A/C will toggle it's power.
// 32-30: Mode - Cool, heat etc.
// 29-28: Fan - Low, medium etc.
// 27-26: Zeros
//    25: Swing On/Off
//    24: iFeel On/Off
//    23: Zero
// 22-19: Temperature, where 15 is 0000, 30 is 1111
//    18: Sleep mode On/Off
// 17- 2: Zeros
//     1: One
//     0: Zero
typedef union ElectraCode {
    uint64_t num;
    struct {
        uint64_t zeros1 : 1;
        uint64_t ones1 : 1;
        uint64_t zeros2 : 16;
        uint64_t sleep : 1;
        uint64_t temperature : 4;
        uint64_t zeros3 : 1;
        uint64_t ifeel : 1;
        uint64_t swing : 1;
        uint64_t zeros4 : 2;
        uint64_t fan : 2;
        uint64_t mode : 3;
        uint64_t power : 1;
    };
} ElectraCode;

const uint8_t ELECTRA_TEMP_MIN = 16;  // Celsius
const uint8_t ELECTRA_TEMP_MAX = 26;  // Celsius
uint8_t target = 21;

#define ELECTRA_TIME_UNIT 1000
#define ELECTRA_NUM_BITS 34

class ElectraClimate : public climate::Climate, public Component {
 public:
  void setup() override
  {
    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 to be off (before was CLIMATE_MODE_AUTO
      this->mode = climate::CLIMATE_MODE_OFF;

      // initialize target temperature to some value so that it's not NAN
      this->target_temperature = roundf(this->current_temperature);
    }

    this->active_mode_ = this->mode;
  }

  void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
    this->transmitter_ = transmitter;
  }

  void set_supported_cool(bool supported_cool) { this->supported_cool_ = supported_cool; }
  void set_supported_heat(bool supported_heat) { this->supported_heat_ = supported_heat; }
  void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }

  /// Override control to change settings of the climate device
  void control(const climate::ClimateCall &call) override
  {
    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();
//new
  if (call.get_fan_mode().has_value())
    this->fan_mode = *call.get_fan_mode();
// end new
    this->transmit_state_();
    this->publish_state();

    this->active_mode_ = this->mode;
  }

  /// Return the traits of this controller
/*   climate::ClimateTraits traits() override
  {
    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(ELECTRA_TEMP_MIN);
    traits.set_visual_max_temperature(ELECTRA_TEMP_MAX);
    traits.set_visual_temperature_step(1);
    return traits;
  }
 */
climate::ClimateTraits traits() override
{
  auto traits = climate::ClimateTraits();
  traits.set_supports_current_temperature(this->sensor_ != nullptr);
  traits.set_supported_modes({ climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_OFF });
  
// added the below line for having support for the fans modes as from climate_traits.h
  traits.set_supported_fan_modes({ climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH });

  traits.set_supports_two_point_target_temperature(false);
  //traits.set_supports_away(false);
  traits.set_visual_min_temperature(ELECTRA_TEMP_MIN);
  traits.set_visual_max_temperature(ELECTRA_TEMP_MAX);
  traits.set_visual_temperature_step(1);
  return traits;
}

  /// Transmit the state of this climate controller via IR
  void transmit_state_()
  {
    ElectraCode code = { 0 };
    code.ones1 = 1;
// original before the switch    code.fan = IRElectraFan::IRElectraFanAuto;
	
/// below is for adding the fan mode
    switch (this->fan_mode.value()) {
      case climate::CLIMATE_FAN_LOW:
        code.fan = IRElectraFan::IRElectraFanLow;
        break;
      case climate::CLIMATE_FAN_MEDIUM:
        code.fan = IRElectraFan::IRElectraFanMedium;
        break;
      case climate::CLIMATE_FAN_HIGH:
        code.fan = IRElectraFan::IRElectraFanHigh;
        break;
      case climate::CLIMATE_FAN_ON:
      default:
        code.fan = IRElectraFan::IRElectraFanLow;// original IRElectraFanAuto
        break;
    }
/// above is for adding the fan mode

    switch (this->mode) {
      case climate::CLIMATE_MODE_COOL:
        code.mode = IRElectraMode::IRElectraModeCool;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_HEAT:
        code.mode = IRElectraMode::IRElectraModeHeat;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_AUTO:
        code.mode = IRElectraMode::IRElectraModeAuto;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_OFF:
      default:
        code.mode = IRElectraMode::IRElectraModeOff;
        break;
    }
    target = (uint8_t) this->target_temperature;
    auto temp = (uint8_t) roundf(clamp(target, ELECTRA_TEMP_MIN, ELECTRA_TEMP_MAX));
    code.temperature = temp - 15;

    ESP_LOGD(TAG, "Sending electra code: %lld", code.num);

    auto transmit = this->transmitter_->transmit();
    auto data = transmit.get_data();

    data->set_carrier_frequency(38000);
    uint16_t repeat = 3;

    for (uint16_t r = 0; r < repeat; r++) {
      // Header
      data->mark(3 * ELECTRA_TIME_UNIT);
      uint16_t next_value = 3 * ELECTRA_TIME_UNIT;
      bool is_next_space = true;

      // Data
      for (int j = ELECTRA_NUM_BITS - 1; j>=0; j--)
      {
        uint8_t bit = (code.num >> j) & 1;

        // if current index is SPACE
        if (is_next_space) {
          // one is one unit low, then one unit up
          // since we're pointing at SPACE, we should increase it by a unit
          // then add another MARK unit
          if (bit == 1) {
            data->space(next_value + ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = false;

          } else {
            // we need a MARK unit, then SPACE unit
            data->space(next_value);
            data->mark(ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = true;
          }

        } else {
          // current index is MARK
          
          // one is one unit low, then one unit up
          if (bit == 1) {
            data->mark(next_value);
            data->space(ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = false;

          } else {
            data->mark(next_value + ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = true;
          }
        }
      }

      // Last value must be SPACE
      data->space(next_value);
    }

    // Footer
    data->mark(4 * ELECTRA_TIME_UNIT);

    transmit.perform();
  }

/* these are new rows for getting the IR reciever data - original from coolix.h
  /// Handle received IR Buffer
  static bool on_electra(climate::Climate *parent, remote_base::RemoteReceiveData data);
  bool on_receive(remote_base::RemoteReceiveData data) override { return ElectraClimate::on_electra(this, data); }

///original 
///  bool on_receive(remote_base::RemoteReceiveData data) override { return CoolixClimate::on_coolix(this, data); }

 */


  ClimateMode active_mode_;

  bool supported_cool_{true};
  bool supported_heat_{true};

  remote_transmitter::RemoteTransmitterComponent *transmitter_;
  sensor::Sensor *sensor_{nullptr};
};

@addadi
Copy link

addadi commented Jan 25, 2024

can anyone share example of full yaml file? thanks

@schlumm
Copy link

schlumm commented Feb 7, 2024

I used esp8266-01, so the pins are for this chip.

captive_portal:
sensor:
  - platform: dht
    model: DHT22
    pin: 0
    id: dht_sensor
    temperature:
      name: "AC Parents Temperature"
      id: dht_temp
    humidity:
      name: "AC Parents Humidity"
    update_interval: 60s  

remote_transmitter:
  pin: 3  #RX pin
  carrier_duty_percent: 50%
  id: my_ir_transmitter

remote_receiver:
  pin: 
    number: 1 #TX pin
    inverted: True
    mode: INPUT_PULLUP
  id: ir_receiver
  dump: raw
  tolerance: 55%
 
climate:
  - platform: custom
    lambda: |-
      auto electra_climate = new ElectraClimate();
      electra_climate->set_sensor(id(dht_temp)); // Optional
      electra_climate->set_transmitter(id(my_ir_transmitter));
      App.register_component(electra_climate);
      return {electra_climate};

    climates:
      - name: "Parents AC"
        id: climate_id

@BenJamesAndo
Copy link

I have a Rinnai AC which uses the ELECTRA_AC IR codes. As mentioned here Ballu also uses ELECTRA_AC IR codes. I was able to use Ballu as the IR Remote Climate in ESPHome for sending and receiving.

climate:
  - platform: ballu
    name: "ESPHome AC Rinnai"
    receiver_id: receiver_ir

remote_transmitter:
    pin: GPIO2
    carrier_duty_percent: 50%

remote_receiver:
  - id: receiver_ir
    dump: all
    pin:
      number: GPIO23
      inverted: true
      mode:
        input: true
        pullup: true
    tolerance: 55%

@mickeyva
Copy link

I used esp8266-01, so the pins are for this chip.

captive_portal:
sensor:
  - platform: dht
    model: DHT22
    pin: 0
    id: dht_sensor
    temperature:
      name: "AC Parents Temperature"
      id: dht_temp
    humidity:
      name: "AC Parents Humidity"
    update_interval: 60s  

remote_transmitter:
  pin: 3  #RX pin
  carrier_duty_percent: 50%
  id: my_ir_transmitter

remote_receiver:
  pin: 
    number: 1 #TX pin
    inverted: True
    mode: INPUT_PULLUP
  id: ir_receiver
  dump: raw
  tolerance: 55%
 
climate:
  - platform: custom
    lambda: |-
      auto electra_climate = new ElectraClimate();
      electra_climate->set_sensor(id(dht_temp)); // Optional
      electra_climate->set_transmitter(id(my_ir_transmitter));
      App.register_component(electra_climate);
      return {electra_climate};

    climates:
      - name: "Parents AC"
        id: climate_id

@schlumm I can only get fan and mode but don't have any degree +- interface...

@omersht
Copy link

omersht commented Oct 2, 2024

I am too not getting any temp control, have you managed to solve that?

@mickeyva
Copy link

mickeyva commented Oct 2, 2024

No. can only power it on and off...I ordered a broadlink and will try smart ir ...

@omersht
Copy link

omersht commented Oct 2, 2024

If you have a zigbee network, you can order any super cheap zigbee ir emitter from aliexpress, there are even some battery powered ones, and integrate them locally using zigbee2mqtt, if you do so, i have already learned all rc-3 commands, and set up using json sensor, and climate template for my other electra ac, i can help you set it up.
Also, smartIR dosent work on electra AC’s because of the wired fact that a change temp command doesn’t turn on the AC.

@omersht
Copy link

omersht commented Oct 2, 2024

I have solved the no temp control issue, add webui, then set temp from webui, then you can delete it, and control temp from home assistant.

@mickeyva
Copy link

mickeyva commented Oct 2, 2024

I have solved the no temp control issue, add webui, then set temp from webui, then you can delete it, and control temp from home assistant.

Great. Can you post your ESPHome yaml file and ElectraClimate.h? can you be more detailed about the above procedure?

@omersht
Copy link

omersht commented Oct 5, 2024

I have Made Some tweaks: 1. fixing the no temp control(hopefully), 2. removeing the non used fan off/on modes,3. add fan auto,4. added swing, 5. changed default max temp to 30C , 6. fixed the off command and 7. added the fan only and dry modes.
At some point custom componenets are supposed to be deprecated, so this shoud be made in to an extarenl component, but this is a project for another day.

#include "esphome.h"

static const char *TAG = "electra.climate";

typedef enum IRElectraMode {
    IRElectraModeCool = 0b001,
    IRElectraModeHeat = 0b010,
    IRElectraModeAuto = 0b011,
    IRElectraModeDry  = 0b100,
    IRElectraModeFan  = 0b101,
    IRElectraModeOff  = 0b001
} IRElectraMode;

typedef enum IRElectraFan {
    IRElectraFanLow    = 0b00,
    IRElectraFanMedium = 0b01,
    IRElectraFanHigh   = 0b10,
    IRElectraFanAuto   = 0b11
} IRElectraFan;

// That configuration has a total of 34 bits
//    33: Power bit, if this bit is ON, the A/C will toggle it's power.
// 32-30: Mode - Cool, heat etc.
// 29-28: Fan - Low, medium etc.
// 27-26: Zeros
//    25: Swing On/Off
//    24: iFeel On/Off
//    23: Zero
// 22-19: Temperature, where 15 is 0000, 30 is 1111
//    18: Sleep mode On/Off
// 17- 2: Zeros
//     1: One
//     0: Zero
typedef union ElectraCode {
    uint64_t num;
    struct {
        uint64_t zeros1 : 1;
        uint64_t ones1 : 1;
        uint64_t zeros2 : 16;
        uint64_t sleep : 1;
        uint64_t temperature : 4;
        uint64_t zeros3 : 1;
        uint64_t ifeel : 1;
        uint64_t swing : 1;
        uint64_t zeros4 : 2;
        uint64_t fan : 2;
        uint64_t mode : 3;
        uint64_t power : 1;
    };
} ElectraCode;

const uint8_t ELECTRA_TEMP_MIN = 16;  // Celsius
const uint8_t ELECTRA_TEMP_MAX = 30;  // Celsius
uint8_t target = 21;

#define ELECTRA_TIME_UNIT 1000
#define ELECTRA_NUM_BITS 34

class ElectraClimate : public climate::Climate, public Component {
 public:
  void setup() override
  {
    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 to be off (before was CLIMATE_MODE_AUTO
      this->mode = climate::CLIMATE_MODE_OFF;

      this->swing_mode = climate::CLIMATE_SWING_OFF;

      // initialize target temperature to some value so that it's not NAN
      this->target_temperature = 24;
    }

    this->active_mode_ = this->mode;
  }
  

  void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
    this->transmitter_ = transmitter;
  }

  void set_supported_cool(bool supported_cool) { this->supported_cool_ = supported_cool; }
  void set_supported_heat(bool supported_heat) { this->supported_heat_ = supported_heat; }
  void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }

  /// Override control to change settings of the climate device
  void control(const climate::ClimateCall &call) override
  {
    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();
//new
  if (call.get_fan_mode().has_value())
    this->fan_mode = *call.get_fan_mode();
// end new

// New: Handle Swing Mode
  if (call.get_swing_mode().has_value()) {
    if (*call.get_swing_mode() == climate::CLIMATE_SWING_OFF)
      this->swing_mode = climate::CLIMATE_SWING_OFF;  // Swing off
    else
      this->swing_mode = climate::CLIMATE_SWING_VERTICAL;   // Swing on
  }


    this->transmit_state_();
    this->publish_state();

    this->active_mode_ = this->mode;
  }

  /// Return the traits of this controller
/*   climate::ClimateTraits traits() override
  {
    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(ELECTRA_TEMP_MIN);
    traits.set_visual_max_temperature(ELECTRA_TEMP_MAX);
    traits.set_visual_temperature_step(1);
    return traits;
  }
 */
climate::ClimateTraits traits() override
{
  auto traits = climate::ClimateTraits();
  traits.set_supports_current_temperature(this->sensor_ != nullptr);
  traits.set_supported_modes({ climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_OFF });
  
// added the below line for having support for the fans modes as from climate_traits.h
  traits.set_supported_fan_modes({ climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH });
// added support for swing modes
  traits.set_supported_swing_modes({ climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL });

  traits.set_supports_two_point_target_temperature(false);
  //traits.set_supports_away(false);
  traits.set_visual_min_temperature(ELECTRA_TEMP_MIN);
  traits.set_visual_max_temperature(ELECTRA_TEMP_MAX);
  traits.set_visual_temperature_step(1);
  return traits;
}

  /// Transmit the state of this climate controller via IR
  void transmit_state_()
  {
    ElectraCode code = { 0 };
    code.ones1 = 1;
// original before the switch    code.fan = IRElectraFan::IRElectraFanAuto;

// Set swing bit based on the swing mode
    if (this->swing_mode) {
      code.swing = 1;  // Swing ON
    } else {
      code.swing = 0;  // Swing OFF
    }
	
/// below is for adding the fan mode
    switch (this->fan_mode.value()) {
      case climate::CLIMATE_FAN_LOW:
        code.fan = IRElectraFan::IRElectraFanLow;
        break;
      case climate::CLIMATE_FAN_MEDIUM:
        code.fan = IRElectraFan::IRElectraFanMedium;
        break;
      case climate::CLIMATE_FAN_HIGH:
        code.fan = IRElectraFan::IRElectraFanHigh;
        break;
      case climate::CLIMATE_FAN_AUTO:  //Added this for Auto Fan Mode
        code.fan = IRElectraFan::IRElectraFanAuto;
        break;
      default:
        code.fan = IRElectraFan::IRElectraFanAuto;// original IRElectraFanAuto
        break;
    }
/// above is for adding the fan mode

    switch (this->mode) {
      case climate::CLIMATE_MODE_COOL:
        code.mode = IRElectraMode::IRElectraModeCool;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_HEAT:
        code.mode = IRElectraMode::IRElectraModeHeat;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_FAN_ONLY:
        code.mode = IRElectraMode::IRElectraModeFan;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_AUTO:
        code.mode = IRElectraMode::IRElectraModeAuto;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_DRY:
        code.mode = IRElectraMode::IRElectraModeDry;
        code.power = this->active_mode_ == climate::CLIMATE_MODE_OFF ? 1 : 0;
        break;
      case climate::CLIMATE_MODE_OFF:
      default:
        code.mode = IRElectraMode::IRElectraModeOff;
        code.power = 1;
        break;
    }
    target = (uint8_t) this->target_temperature;
    auto temp = (uint8_t) roundf(clamp(target, ELECTRA_TEMP_MIN, ELECTRA_TEMP_MAX));
    code.temperature = temp - 15;

    ESP_LOGD(TAG, "Sending electra code: %lld", code.num);

    auto transmit = this->transmitter_->transmit();
    auto data = transmit.get_data();

    data->set_carrier_frequency(38000);
    uint16_t repeat = 3;

    for (uint16_t r = 0; r < repeat; r++) {
      // Header
      data->mark(3 * ELECTRA_TIME_UNIT);
      uint16_t next_value = 3 * ELECTRA_TIME_UNIT;
      bool is_next_space = true;

      // Data
      for (int j = ELECTRA_NUM_BITS - 1; j>=0; j--)
      {
        uint8_t bit = (code.num >> j) & 1;

        // if current index is SPACE
        if (is_next_space) {
          // one is one unit low, then one unit up
          // since we're pointing at SPACE, we should increase it by a unit
          // then add another MARK unit
          if (bit == 1) {
            data->space(next_value + ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = false;

          } else {
            // we need a MARK unit, then SPACE unit
            data->space(next_value);
            data->mark(ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = true;
          }

        } else {
          // current index is MARK
          
          // one is one unit low, then one unit up
          if (bit == 1) {
            data->mark(next_value);
            data->space(ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = false;

          } else {
            data->mark(next_value + ELECTRA_TIME_UNIT);
            next_value = ELECTRA_TIME_UNIT;
            is_next_space = true;
          }
        }
      }

      // Last value must be SPACE
      data->space(next_value);
    }

    // Footer
    data->mark(4 * ELECTRA_TIME_UNIT);

    transmit.perform();
  }
/*
  ///these are new rows for getting the IR reciever data - original from coolix.h
  /// Handle received IR Buffer
  static bool on_electra(climate::Climate *parent, remote_base::RemoteReceiveData data);
  bool on_receive(remote_base::RemoteReceiveData data) override { return ElectraClimate::on_electra(this, data); }

///original 
///  bool on_receive(remote_base::RemoteReceiveData data) override { return CoolixClimate::on_coolix(this, data); }
*/



  ClimateMode active_mode_;

  bool supported_cool_{true};
  bool supported_heat_{true};

  remote_transmitter::RemoteTransmitterComponent *transmitter_;
  sensor::Sensor *sensor_{nullptr};
};

yaml is the same as the original:

captive_portal:


remote_transmitter:
 pin: 26
 carrier_duty_percent: 97%
 id: ir_transmitter

light:
  - platform: status_led
    name: "Indecator LED"
    pin: 8

climate:
  - platform: custom
    lambda: |-
      auto electra_climate = new ElectraClimate();
      electra_climate->set_transmitter(id(ir_transmitter));
      App.register_component(electra_climate);
      return {electra_climate};

    climates:
      - name: "Basement Aircon"

@mickeyva
Copy link

mickeyva commented Oct 6, 2024

Working great! now I don't know what to do with my broadlinks I ordered :)

@omersht
Copy link

omersht commented Oct 7, 2024

@mickeyva
Copy link

mickeyva commented Oct 7, 2024

Did you personally utilized a temperature sensor?

@omersht
Copy link

omersht commented Oct 7, 2024

No

@omersht
Copy link

omersht commented Oct 7, 2024

But it’s possible

@omersht
Copy link

omersht commented Oct 13, 2024

Can anyone knows how to add the support for the IR receiver, so a change with the regular remote will be reflected in HA?

Yes, in the github repository I have made https://github.com/omersht/ElectraEspHome

@schlumm
Copy link

schlumm commented Nov 3, 2024

i added the option for decode the IR signal, in case that someone is using the old IR transmitter.

/* #include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std; */

/* struct ac_code {
    int fan;
    int mode;
    int temp;
}; */

// A function to check if a value is within 5% tolerance of a target value
bool within_tolerance(int value, int target) {
    return std::abs(value - target) <= (0.05 * target);
}

std::vector<int> decode_ir_signal(std::vector<int>& vec) {
    // A constant to store the number of bits in the code
    const int ELECTRA_NUM_BITS2 = 34;
    std::vector<int> decoded_bits(ELECTRA_NUM_BITS2, 0); // bit has ELECTRA_NUM_BITS elements, all equal to 0
    
    //cout << "\n"; // print a newline

    // A constant to store the time unit in microseconds
    const int ELECTRA_TIME_UNIT2 = 1000;
    // A variable to store the next value to be pushed into the vector
    int next_value = 3 * ELECTRA_TIME_UNIT2;
    // A boolean flag to indicate whether the next value is a space or not
    bool is_next_space = true;
    int code_index = 0;

    for (int j = 1; j < vec.size() - 1; j++) {
        if (j > 5 && std::abs(vec[j]) > 2800) {
            break;
        }

        int x = vec[j];

        // if current index is SPACE
        if (is_next_space) {
            if (within_tolerance(abs(vec[j]), next_value + ELECTRA_TIME_UNIT2)) {
                decoded_bits[code_index] = 1;
/*                 cout << "s1b1 ";
                cout << "\n"; // print a newline */
                is_next_space = false;
                next_value = ELECTRA_TIME_UNIT2;
            } else {
                decoded_bits[code_index] = 0;
/*                 cout << "s1b0 ";
                cout << "\n"; // print a newline */
                next_value = ELECTRA_TIME_UNIT2;
                is_next_space = true;
                j = j + 1;
            }
        } else {
            if (within_tolerance(abs(vec[j]), next_value)) {
                decoded_bits[code_index] = 1;
/*                 cout << "s0b1 ";
                cout << "\n"; // print a newline */
                j = j + 1;
                next_value = ELECTRA_TIME_UNIT2;
                is_next_space = false;
            } else {
                decoded_bits[code_index] = 0;
/*                 cout << "s0b0 ";
                cout << "\n"; // print a newline */
                next_value = ELECTRA_TIME_UNIT2;
                is_next_space = true;
            }
        }
        code_index = code_index + 1;
    }

    return decoded_bits;
}

the yaml should be like this:

esphome:
  name: ac-parents-electra
  friendly_name: Parents AC
  includes:
    - ElectraClimate.h
    - ElectraDecodeFinal.h

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "UbGyg4="

ota:
  - platform: esphome
    password: "75ff13a"

wifi:
  networks:
    - ssid: !secret wifi_ssid
      password: !secret wifi_password
    - ssid: !secret wifi_ssid2
      password: !secret wifi_password2
    
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Ac-Parents-Electra"
    password: "ySYKNFeRJZk2"

captive_portal:

sensor:
  - platform: dht
    model: DHT22
    pin: 0
    id: dht_sensor
    temperature:
      name: "AC Parents Temperature"
      id: dht_temp
    humidity:
      name: "AC Parents Humidity"
    update_interval: 60s  

remote_transmitter:
  pin: 3  # RX pin
  carrier_duty_percent: 50%
  id: my_ir_transmitter

remote_receiver:
  pin: 
    number: 1  # TX pin
    inverted: True
    mode: INPUT_PULLUP
  id: ir_receiver
  dump: raw
  tolerance: 55%
  filter: 500us
  on_raw:
    then:
      - lambda: |-
          if ((std::abs(x[0]) > 2800) && (std::abs(x[1]) > 2800)) {// change the 2nd pulse to 2800 to includes bit power=0

            // Log the current AC status
            ElectraCode current;
            switch (id(climate_id).mode) {
              case climate::CLIMATE_MODE_COOL:
                current.mode=IRElectraMode::IRElectraModeCool;
                break;
              case climate::CLIMATE_MODE_AUTO:
                current.mode = IRElectraMode::IRElectraModeAuto;
                break;
              case climate::CLIMATE_MODE_HEAT:
                current.mode=IRElectraMode::IRElectraModeHeat;
                break;
              case climate::CLIMATE_MODE_OFF:
                current.mode = IRElectraMode::IRElectraModeOff;
                break;
              case climate::CLIMATE_MODE_DRY:
                current.mode = IRElectraMode::IRElectraModeDry;
                break;
            }

            switch (id(climate_id).fan_mode.value()) {
              case climate::CLIMATE_FAN_LOW:
                current.fan=IRElectraFan::IRElectraFanLow;
                break;
              case climate::CLIMATE_FAN_MEDIUM:
                current.fan = IRElectraFan::IRElectraFanMedium;
                break;
              case climate::CLIMATE_FAN_HIGH:
                current.fan=IRElectraFan::IRElectraFanHigh;
                break;
            }
            
            ESP_LOGD("climate", "Current AC mode: %d", current.mode);
            ESP_LOGD("climate", "Current Fan mode: %d", current.fan);
            // Log the current target_temperature
            float current_target_temperature = id(climate_id).target_temperature;
            ESP_LOGD("climate", "Current AC target Temperature: %.1f°C", current_target_temperature);

            // decodeding the IR signal
            auto decoded_bits = decode_ir_signal(x);           
            auto mode =0;
            for (int i = 1; i <= 3; i++) {
                mode = mode << 1 | (decoded_bits[i] & 1);
            }
            int target_temp=(15 + 8 * decoded_bits[11] + 4 * decoded_bits[12] + 2 * decoded_bits[13] + decoded_bits[14]);
            ESP_LOGD("IR Receiver", " target_temp- dec: %lld", target_temp);
            
            ElectraCode code;
            code.num = 0;
            for (int i = 0; i < decoded_bits.size(); i++) {
              code.num = code.num << 1 | (decoded_bits[i] & 1);
            }// code.num is reversed of the string decoded_bits
            ESP_LOGD("IR Receiver", "Received code dec: %lld", code.num);
            ESP_LOGD("IR Receiver", "AC mode from the code: %lld", code.mode);
            ESP_LOGD("IR Receiver", "AC fan from the code: %lld", code.fan);
            ESP_LOGD("IR Receiver", "AC target Temperature from the code: %lld", code.temperature+15);

            // update the target temperature and fan no matter what is the code.power state is
              if(code.temperature>0){// In case that the recieved code is not nonsense
                id(climate_id).target_temperature = code.temperature + 15;//Temperature, where 15 is 0000, 30 is 1111
              }
              switch (code.fan) {
                  case 0:
                    id(climate_id).fan_mode = esphome::climate::CLIMATE_FAN_LOW;
                    break;
                  case 1:
                    id(climate_id).fan_mode = esphome::climate::CLIMATE_FAN_MEDIUM;
                    break;
                  case 2:
                    id(climate_id).fan_mode = esphome::climate::CLIMATE_FAN_HIGH;
                    break;
                  case 3:
                    id(climate_id).fan_mode = esphome::climate::CLIMATE_FAN_ON;
                    break;
                  default:
                    id(climate_id).fan_mode.reset();  // Optional: if the value is out of range, reset the optional
                    break;
                }

            if (code.power){
              if (id(climate_id).mode!=climate::CLIMATE_MODE_OFF) {
                id(climate_id).mode=climate::CLIMATE_MODE_OFF;
              }
              else {           
                if (code.mode == IRElectraMode::IRElectraModeCool) {
                    id(climate_id).mode = climate::CLIMATE_MODE_COOL;
                  } else if (code.mode == IRElectraMode::IRElectraModeHeat) {
                    id(climate_id).mode = climate::CLIMATE_MODE_HEAT;
                  } else if (code.mode == IRElectraMode::IRElectraModeAuto) {
                    id(climate_id).mode = climate::CLIMATE_MODE_AUTO;
                  } else if (code.mode == IRElectraMode::IRElectraModeDry) {
                    id(climate_id).mode = climate::CLIMATE_MODE_DRY;
                  } else if (code.mode == IRElectraMode::IRElectraModeFan) {
                    id(climate_id).mode = climate::CLIMATE_MODE_FAN_ONLY;
                  } else if (code.mode == IRElectraMode::IRElectraModeOff) {
                    id(climate_id).mode = climate::CLIMATE_MODE_OFF;
                  } else if (code.mode == 0) {
                    id(climate_id).mode = climate::CLIMATE_MODE_OFF;
                  } 
                }
                ESP_LOGD("IR Receiver", "In the if, so something was changed - mode,fan or temp (code.mode=1 and mode !=off)");

              // if((id(climate_id).mode ==code.mode )&&(id(climate_id).target_temperature==code.temperature + 15)&&(id(climate_id).fan_mode==code.fan)){
              //   ESP_LOGD("IR Receiver", "Nothing was changed - mode,fan or temp");
              //   id(climate_id).mode = climate::CLIMATE_MODE_OFF;
              // }
              }
            else {//code.power=0
              ESP_LOGD("IR Receiver", "In the else statment (code.power=0). no update to the mode");
              }
            id(climate_id).publish_state();
            ESP_LOGD("IR Receiver", "Publish the state");    
            delay (5000) ;//delay in order not to includes multiple IR signal of the same state
          }
climate:
  - platform: custom
    lambda: |-
      auto electra_climate = new ElectraClimate();
      electra_climate->set_sensor(id(dht_temp)); // Optional
      electra_climate->set_transmitter(id(my_ir_transmitter));
      App.register_component(electra_climate);
      return {electra_climate};

    climates:
      - name: "Parents AC"
        id: climate_id

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment