Files
esphome_aux_ac_component/components/aux_ac/aircon.cpp
2024-02-25 00:44:28 +03:00

754 lines
36 KiB
C++

#include "aircon.h"
#include "helpers.h"
#include "command_builder.h"
#include "frame_processor_manager.h"
#include "esphome/core/log.h"
namespace esphome
{
namespace aux_airconditioner
{
/*************************************************************************************************\
\*************************************************************************************************/
uint32_t Capabilities::normilize_packet_timeout(uint32_t timeout)
{
uint32_t result = timeout;
if (result > Capabilities::AC_PACKET_TIMEOUT_MAX)
result = Capabilities::AC_PACKET_TIMEOUT_MAX;
else if (result < Capabilities::AC_PACKET_TIMEOUT_MIN)
result = Capabilities::AC_PACKET_TIMEOUT_MIN;
return result;
}
float Capabilities::normilize_target_temperature(const float target_temperature)
{
float result = target_temperature;
if (result > Capabilities::AC_MAX_TEMPERATURE)
result = Capabilities::AC_MAX_TEMPERATURE;
else if (result < Capabilities::AC_MIN_TEMPERATURE)
result = Capabilities::AC_MIN_TEMPERATURE;
return result;
}
uint8_t Capabilities::normilize_inverter_power_limit(const uint8_t power_limit_value)
{
uint8_t result = power_limit_value;
if (result > Capabilities::AC_MAX_INVERTER_POWER_LIMIT)
result = Capabilities::AC_MAX_INVERTER_POWER_LIMIT;
else if (result < Capabilities::AC_MIN_INVERTER_POWER_LIMIT)
result = Capabilities::AC_MIN_INVERTER_POWER_LIMIT;
return result;
}
// **************************************************************************************************
const std::string Capabilities::AC_FIRMWARE_VERSION = "1.0.0";
// **************************************************************************************************
// custom fan modes
const std::string Capabilities::CUSTOM_FAN_MODE_MUTE = "MUTE";
const std::string Capabilities::CUSTOM_FAN_MODE_TURBO = "TURBO";
// **************************************************************************************************
// custom presets
const std::string Capabilities::CUSTOM_PRESET_CLEAN = "CLEAN";
const std::string Capabilities::CUSTOM_PRESET_HEALTH = "HEALTH";
const std::string Capabilities::CUSTOM_PRESET_ANTIFUNGUS = "ANTIFUNGUS";
// **************************************************************************************************
// predefined default params
const float Capabilities::AC_MIN_TEMPERATURE = 16.0;
const float Capabilities::AC_MAX_TEMPERATURE = 32.0;
const float Capabilities::AC_TEMPERATURE_STEP_TARGET = 0.5;
const float Capabilities::AC_TEMPERATURE_STEP_CURRENT = 0.1;
const uint8_t Capabilities::AC_MIN_INVERTER_POWER_LIMIT = 30; // 30%
const uint8_t Capabilities::AC_MAX_INVERTER_POWER_LIMIT = 100; // 100%
const uint32_t Capabilities::AC_STATE_REQUEST_INTERVAL = 7000;
const uint32_t Capabilities::AC_CONNECTION_LOST_TIMEOUT = 4000;
const uint32_t Capabilities::AC_PACKET_TIMEOUT_MIN = 300;
const uint32_t Capabilities::AC_PACKET_TIMEOUT_MAX = 800;
// **************************************************************************************************
using esphome::helpers::update_property;
// **************************************************************************************************
void AirCon::_send_frame_from_tx_queue()
{
if (!this->is_hardware_connected() || !this->has_ping())
return;
if (this->_tx_frames.empty())
return;
Frame *frame = this->_tx_frames.front();
this->_tx_frames.pop();
frame->send(*this->_uart);
if (frame->get_frame_type() == FrameType::FRAME_TYPE_COMMAND)
{
_waiting_for_response_timer.reset();
_waiting_for_response_timer.set_callback([this](TimerInterface *timer)
{ ESP_LOGW(TAG, "Command response timeout!");
timer->stop();
this->set_receiver_callback(nullptr); });
switch (frame->get_value(8))
{
case 0x01: // command: set AC mode
{
uint8_t crc16_1 = 0, crc16_2 = 0;
frame->get_crc(crc16_1, crc16_2);
this->set_receiver_callback([this, crc16_1, crc16_2](Frame &frame)
{ if (this->_waiting_for_response_timer.is_enabled() &&
frame.get_frame_type() == FrameType::FRAME_TYPE_RESPONSE &&
frame.get_value(9) == 0x01)
{
// check the acknowledgement: should be equal to the command CRC
if (frame.get_value(10) != crc16_1 ||
frame.get_value(11) != crc16_2)
{
ESP_LOGW(TAG, "Command response acknowledgement error!");
}
this->_waiting_for_response_timer.stop();
this->_waiting_for_response_timer.set_callback(helpers::dummy_stopper);
this->set_receiver_callback(nullptr);
if (!this->_command_queue.empty() &&
this->_cmd_processor_state == command_processor_state_t::CMD_PROCESSOR_STATE_CMD_WAS_SENT)
{
this->_cmd_processor_state = command_processor_state_t::CMD_PROCESSOR_STATE_POSTCHECK_DONE;
}
} });
_waiting_for_response_timer.start(this->get_packet_timeout());
break;
}
case 0x11: // command: request frame 11
{
this->set_receiver_callback([this](Frame &frame)
{ if (this->_waiting_for_response_timer.is_enabled() &&
frame.get_frame_type() == FrameType::FRAME_TYPE_RESPONSE &&
frame.get_value(9) == 0x11)
{
this->_waiting_for_response_timer.stop();
this->_waiting_for_response_timer.set_callback(helpers::dummy_stopper);
this->set_receiver_callback(nullptr);
if (!this->_command_queue.empty() &&
this->_cmd_processor_state == command_processor_state_t::CMD_PROCESSOR_STATE_WAITING_FOR_F11)
{
this->_cmd_processor_state = command_processor_state_t::CMD_PROCESSOR_STATE_PRECHECK_DONE;
}
} });
_waiting_for_response_timer.start(this->get_packet_timeout());
break;
}
case 0x21: // command: request frame 21
{
this->set_receiver_callback([this](Frame &frame)
{ if (this->_waiting_for_response_timer.is_enabled() &&
frame.get_frame_type() == FrameType::FRAME_TYPE_RESPONSE &&
frame.get_value(9) == 0x21)
{
this->_waiting_for_response_timer.stop();
this->_waiting_for_response_timer.set_callback(helpers::dummy_stopper);
this->set_receiver_callback(nullptr);
} });
_waiting_for_response_timer.start(this->get_packet_timeout());
break;
}
default:
ESP_LOGW(TAG, "Unknown command: 0x%02X", frame->get_value(8));
this->_waiting_for_response_timer.set_callback(helpers::dummy_stopper);
break;
}
}
delete frame;
}
// **************************************************************************************************
void AirCon::_process_command_queue()
{
if (_command_queue.size() == 0)
return;
Frame frame;
ClimateCall &cmd = _command_queue.front();
switch (this->_cmd_processor_state)
{
case CMD_PROCESSOR_STATE_NOT_STARTED:
this->_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_REQUEST_11).fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
this->_cmd_processor_state = command_processor_state_t::CMD_PROCESSOR_STATE_WAITING_FOR_F11;
break;
case CMD_PROCESSOR_STATE_WAITING_FOR_F11:
ESP_LOGW(TAG, "It should never have happened: processing the command with state 'CMD_PROCESSOR_STATE_WAITING_FOR_F11'");
break;
case CMD_PROCESSOR_STATE_PRECHECK_DONE:
this->_cmd_builder->init_new_command(cmd).fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
this->_cmd_processor_state = command_processor_state_t::CMD_PROCESSOR_STATE_CMD_WAS_SENT;
break;
case CMD_PROCESSOR_STATE_CMD_WAS_SENT:
ESP_LOGW(TAG, "It should never have happened: processing the command with state 'CMD_PROCESSOR_STATE_CMD_WAS_SENT'");
break;
case CMD_PROCESSOR_STATE_POSTCHECK_DONE:
this->schedule_frame_to_send(*_frame_11_request);
this->schedule_frame_to_send(*_frame_2x_request);
this->_cmd_processor_state = command_processor_state_t::CMD_PROCESSOR_STATE_NOT_STARTED;
_command_queue.pop();
break;
default:
ESP_LOGW(TAG, "unknown command state '0x%02X'!", this->_cmd_processor_state);
this->_cmd_processor_state = command_processor_state_t::CMD_PROCESSOR_STATE_NOT_STARTED;
_command_queue.pop();
break;
}
}
// **************************************************************************************************
AirCon::AirCon()
{
_incoming_frame = new Frame;
_last_frame_11 = new Frame;
_last_frame_2x = new Frame;
_frame_processor_manager = new FrameProcessorManager;
_frame_processor_manager->set_aircon(*this);
_cmd_builder = new CommandBuilder(*this);
this->set_millis(&millis);
_timer_manager.set_millis_func(&millis);
_frame_11_request = new Frame;
this->_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_REQUEST_11).fill_frame_with_command(*_frame_11_request);
_frame_2x_request = new Frame;
this->_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_REQUEST_21).fill_frame_with_command(*_frame_2x_request);
_frame_ping_response = new Frame;
_frame_ping_response->append_data({Frame::get_start_byte(), 0x00, FrameType::FRAME_TYPE_PING, FrameDirection::FRAME_DIR_TO_AC, 0x01, 0x00, 0x08, 0x00});
_frame_ping_response->append_data({0x1C, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
_frame_ping_response->update_crc(true);
}
AirCon::~AirCon()
{
delete _cmd_builder;
delete _frame_processor_manager;
delete _incoming_frame;
delete _last_frame_11;
delete _last_frame_2x;
delete _frame_11_request;
delete _frame_2x_request;
delete _frame_ping_response;
}
// **************************************************************************************************
void AirCon::setup()
{
_traits.set_supports_current_temperature(true);
_traits.set_supports_two_point_target_temperature(false);
_traits.set_visual_min_temperature(Capabilities::AC_MIN_TEMPERATURE);
_traits.set_visual_max_temperature(Capabilities::AC_MAX_TEMPERATURE);
_traits.set_visual_current_temperature_step(Capabilities::AC_TEMPERATURE_STEP_CURRENT);
_traits.set_visual_target_temperature_step(Capabilities::AC_TEMPERATURE_STEP_TARGET);
/* + MINIMAL SET */
_traits.add_supported_mode(ClimateMode::CLIMATE_MODE_OFF);
_traits.add_supported_mode(ClimateMode::CLIMATE_MODE_FAN_ONLY);
_traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_AUTO);
_traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_LOW);
_traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_MEDIUM);
_traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_HIGH);
_traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF);
_traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_NONE);
// if the climate device supports reporting the active current action of the device with the action property.
//_traits.set_supports_action(this->_show_action);
_frame11_request_timer.set_callback([this](TimerInterface *timer)
{ this->schedule_frame_to_send(*this->_frame_11_request); });
_timer_manager.register_timer(_frame11_request_timer);
_frame11_request_timer.start(this->get_period());
_frame2x_request_timer.set_callback([this](TimerInterface *timer)
{ this->schedule_frame_to_send(*this->_frame_2x_request); });
_timer_manager.register_timer(_frame2x_request_timer);
_frame2x_request_timer.start(this->get_period());
_ping_timeout_timer.set_callback([this](TimerInterface *timer)
{ this->_has_ping = false;
ESP_LOGW(TAG, "Air conditioner connection lost!"); });
_timer_manager.register_timer(_ping_timeout_timer);
_ping_timeout_timer.start(Capabilities::AC_CONNECTION_LOST_TIMEOUT);
_timer_manager.register_timer(_waiting_for_response_timer);
_waiting_for_response_timer.stop();
// schedule initial requests
this->schedule_frame_to_send(*this->_frame_11_request);
this->schedule_frame_to_send(*this->_frame_2x_request);
}
// **************************************************************************************************
void AirCon::loop()
{
if (!this->is_hardware_connected())
return;
_timer_manager.task();
FrameState frame_state = _incoming_frame->get_frame_state();
switch (frame_state)
{
case FRAME_STATE_BLANK:
case FRAME_STATE_PARTIALLY_LOADED:
if (_uart->available() > 0)
{
_incoming_frame->load(*_uart);
}
else if (!this->_waiting_for_response_timer.is_enabled())
{
if (!this->_tx_frames.empty())
{
this->_send_frame_from_tx_queue();
}
else if (!this->_command_queue.empty())
{
this->_process_command_queue();
}
}
break;
case FRAME_STATE_ERROR:
_incoming_frame->set_frame_time(this->ms());
ESP_LOGW(TAG, "Incorrect frame! Frame state: %s, data: %s", _incoming_frame->state_to_string().c_str(), _incoming_frame->to_string(true).c_str());
_incoming_frame->clear();
break;
case FRAME_STATE_OK:
_incoming_frame->set_frame_time(this->ms());
ESP_LOGD(TAG, "%s", _incoming_frame->to_string(true).c_str());
this->_frame_processor_manager->process_frame(*_incoming_frame);
if (this->_receiver_callback != nullptr)
this->_receiver_callback(*_incoming_frame);
_incoming_frame->clear();
break;
default:
ESP_LOGW(TAG, "Unknown frame state: %d (0x%02X)", frame_state, frame_state);
break;
}
}
// **************************************************************************************************
void AirCon::dump_config()
{
ESP_LOGCONFIG(TAG, "AUX HVAC:");
ESP_LOGCONFIG(TAG, "firmware version: %s", Capabilities::AC_FIRMWARE_VERSION.c_str());
this->dump_traits_(TAG);
LOG_SENSOR(" ", "Vertical louver state", this->_sensor_vlouver_state);
LOG_BINARY_SENSOR(" ", "Display", this->_sensor_display_state);
LOG_BINARY_SENSOR(" ", "Defrost status", this->_sensor_defrost_state);
LOG_TEXT_SENSOR(" ", "Preset Reporter", this->_sensor_preset_reporter);
ESP_LOGCONFIG(TAG, " Temperatures:");
LOG_SENSOR(" ", "Indoor Ambient", this->_sensor_temperature_indoor_ambient);
LOG_SENSOR(" ", "Indoor Coil", this->_sensor_temperature_indoor_coil);
LOG_SENSOR(" ", "Outdoor Ambient", this->_sensor_temperature_outdoor_ambient);
LOG_SENSOR(" ", "Outdoor Condenser", this->_sensor_temperature_outdoor_condenser_middle);
LOG_SENSOR(" ", "Outdoor Defrost", this->_sensor_temperature_outdoor_defrost);
LOG_SENSOR(" ", "Outdoor Discharge", this->_sensor_temperature_outdoor_discharge);
LOG_SENSOR(" ", "Outdoor Suction", this->_sensor_temperature_outdoor_suction);
ESP_LOGCONFIG(TAG, " Inverter Power:");
LOG_SENSOR(" ", "Actual Value", this->_sensor_inverter_power_actual);
LOG_SENSOR(" ", "Limit Value", this->_sensor_inverter_power_limit_value);
LOG_BINARY_SENSOR(" ", "Limitation State", this->_sensor_inverter_power_limit_state);
};
// **************************************************************************************************
void AirCon::control(const esphome::climate::ClimateCall &call)
{
bool has_command = false;
// User requested mode change
if (call.get_mode().has_value())
{
ClimateMode mode = *call.get_mode();
update_property(this->mode, mode, has_command);
}
// User requested fan_mode change
if (call.get_fan_mode().has_value())
{
ClimateFanMode fanmode = *call.get_fan_mode();
update_property(this->fan_mode, fanmode, has_command);
}
else if (call.get_custom_fan_mode().has_value())
{
std::string customfanmode = *call.get_custom_fan_mode();
if ((customfanmode == Capabilities::CUSTOM_FAN_MODE_TURBO) ||
(customfanmode == Capabilities::CUSTOM_FAN_MODE_MUTE) ||
(customfanmode == ""))
{
update_property(this->custom_fan_mode, customfanmode, has_command);
}
}
// User selected preset
if (call.get_preset().has_value())
{
ClimatePreset preset = *call.get_preset();
update_property(this->preset, preset, has_command);
}
else if (call.get_custom_preset().has_value())
{
std::string custom_preset = *call.get_custom_preset();
if ((custom_preset == Capabilities::CUSTOM_PRESET_CLEAN) ||
(custom_preset == Capabilities::CUSTOM_PRESET_ANTIFUNGUS) ||
(custom_preset == Capabilities::CUSTOM_PRESET_HEALTH) ||
(custom_preset == ""))
{
update_property(this->custom_preset, custom_preset, has_command);
}
}
// User requested swing_mode change
if (call.get_swing_mode().has_value())
{
ClimateSwingMode swingmode = *call.get_swing_mode();
update_property(this->swing_mode, swingmode, has_command);
}
// User requested target temperature change
if (call.get_target_temperature().has_value())
{
// it isn't allowed in FAN mode
if (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)
update_property(this->target_temperature, *call.get_target_temperature(), has_command);
}
if (has_command)
{
this->schedule_command(call);
if (this->get_optimistic())
this->publish_all_states();
}
}
// **************************************************************************************************
void AirCon::action_display_off()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_display_state(false);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_display_on()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_display_state(true);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_swing()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_SWING_UPDOWN);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_stop()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_OFF);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_top_position()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_TOP);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_middle_above_position()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_MIDDLE_ABOVE);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_middle_position()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_MIDDLE);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_middle_below_position()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_MIDDLE_BELOW);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_bottom()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(ac_louver_V::AC_LOUVERV_BOTTOM);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_set_vlouver_position(vlouver_esphome_position_t position)
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_vertical_louver(vlouver_frontend_to_ac_louver_V(position));
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_power_limitation_off()
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE).set_inverter_power_limitation_state(false);
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::action_power_limitation_on(uint8_t limit)
{
_cmd_builder->init_new_command(command_type_t::COMMAND_TYPE_SET_STATE)
.set_inverter_power_limitation_state(true)
.set_inverter_power_limitation_value(Capabilities::normilize_inverter_power_limit(limit));
Frame frame;
_cmd_builder->fill_frame_with_command(frame);
this->schedule_frame_to_send(frame);
}
// **************************************************************************************************
void AirCon::schedule_frame_to_send(const Frame &frame)
{
Frame *tx_frame = new Frame(frame);
_tx_frames.push(tx_frame);
}
// **************************************************************************************************
void AirCon::schedule_ping_response()
{
this->schedule_frame_to_send(*_frame_ping_response);
}
// **************************************************************************************************
void AirCon::schedule_command(const ClimateCall &cmd)
{
_command_queue.push(cmd);
}
// **************************************************************************************************
Frame &AirCon::get_last_frame_11()
{
return *(this->_last_frame_11);
}
// **************************************************************************************************
Frame &AirCon::get_last_frame_2x()
{
return *(this->_last_frame_2x);
}
// **************************************************************************************************
void AirCon::set_last_frame(const Frame &frame)
{
if (frame.get_frame_type() != FrameType::FRAME_TYPE_RESPONSE)
return;
if (frame.get_body_length() < 2) // filter out frames without CMD byte
return;
Frame *target_frame = nullptr;
if (frame.get_value(9) == 0x11)
{
target_frame = _last_frame_11;
}
else if (frame.get_value(9, 0b11110000) == 0x20)
{
target_frame = _last_frame_2x;
}
if (target_frame != nullptr)
{
target_frame->clear();
target_frame->append_data(frame.data(), frame.size(), true);
target_frame->set_frame_time(this->ms());
}
}
// **************************************************************************************************
void AirCon::_update_sensor_unit_of_measurement(Sensor *sensor)
{
if (sensor == nullptr)
return;
if (this->temperature_in_fahrenheit && sensor->get_unit_of_measurement() != "°F")
sensor->set_unit_of_measurement("°F");
else if (!this->temperature_in_fahrenheit && sensor->get_unit_of_measurement() != "°C")
sensor->set_unit_of_measurement("°C");
}
void AirCon::update_all_sensors_unit_of_measurement()
{
this->_update_sensor_unit_of_measurement(_sensor_temperature_indoor_ambient);
this->_update_sensor_unit_of_measurement(_sensor_temperature_indoor_coil);
this->_update_sensor_unit_of_measurement(_sensor_temperature_outdoor_condenser_middle);
this->_update_sensor_unit_of_measurement(_sensor_temperature_outdoor_ambient);
this->_update_sensor_unit_of_measurement(_sensor_temperature_outdoor_defrost);
this->_update_sensor_unit_of_measurement(_sensor_temperature_outdoor_discharge);
this->_update_sensor_unit_of_measurement(_sensor_temperature_outdoor_suction);
}
// **************************************************************************************************
template <typename T>
void publish_sensor_state(Sensor *sensor, optional<T> new_state)
{
if (sensor == nullptr)
return;
if (new_state.has_value() && !std::isnan((float)(new_state.value())))
{
if (sensor->get_raw_state() == (float)(new_state.value()))
return;
sensor->publish_state((float)(new_state.value()));
return;
}
if (std::isnan(sensor->get_raw_state()))
return;
sensor->publish_state(NAN);
}
// **************************************************************************************************
void publish_sensor_state(BinarySensor *sensor, optional<bool> new_state)
{
if (sensor == nullptr)
return;
if (!new_state.has_value())
return;
if (sensor->state == new_state.value())
return;
sensor->publish_state(new_state.value());
}
// **************************************************************************************************
void publish_sensor_state(TextSensor *sensor, optional<std::string> new_state)
{
if (sensor == nullptr)
return;
if (!new_state.has_value())
return;
if (sensor->get_raw_state() == new_state.value())
return;
sensor->publish_state(new_state.value());
}
// **************************************************************************************************
void AirCon::publish_all_states()
{
this->publish_state();
publish_sensor_state(_sensor_temperature_indoor_ambient, optional<float>(this->current_temperature));
publish_sensor_state(_sensor_temperature_indoor_coil, this->temperature_indoor_coil);
publish_sensor_state(_sensor_temperature_outdoor_condenser_middle, this->temperature_condenser_middle);
publish_sensor_state(_sensor_temperature_outdoor_ambient, this->temperature_outdoor_ambient);
publish_sensor_state(_sensor_temperature_outdoor_defrost, this->temperature_outdoor_defrost);
publish_sensor_state(_sensor_temperature_outdoor_discharge, this->temperature_outdoor_discharge);
publish_sensor_state(_sensor_temperature_outdoor_suction, this->temperature_outdoor_suction);
publish_sensor_state(_sensor_vlouver_state, optional<uint8_t>(this->get_current_vlouver_frontend_state()));
publish_sensor_state(_sensor_display_state, optional<bool>(this->display_enabled));
publish_sensor_state(_sensor_defrost_state, optional<bool>(this->defrost_enabled));
publish_sensor_state(_sensor_inverter_power_actual, this->inverter_power);
publish_sensor_state(_sensor_inverter_power_limit_value, this->inverter_power_limitation_value);
publish_sensor_state(_sensor_inverter_power_limit_state, this->inverter_power_limitation_on);
std::string state_str = "";
if (this->preset == ClimatePreset::CLIMATE_PRESET_SLEEP)
{
state_str += "SLEEP";
}
else if (this->custom_preset.has_value())
{
state_str += this->custom_preset.value().c_str();
}
else
{
state_str += "NONE";
}
publish_sensor_state(_sensor_preset_reporter, optional<std::string>(state_str));
}
// **************************************************************************************************
// converts vertical louver state from hardware codes to frontend code
vlouver_esphome_position_t AirCon::aux_vlouver_to_frontend(const ac_louver_V vLouver)
{
return ac_louver_V_to_vlouver_frontend(vLouver);
}
// **************************************************************************************************
// current vertical louver position in esphome codes
vlouver_esphome_position_t AirCon::get_current_vlouver_frontend_state()
{
return aux_vlouver_to_frontend(this->louver_vertical);
}
// **************************************************************************************************
// converts vertical louver position from frontend codes to hardware code
ac_louver_V AirCon::frontend_vlouver_to_aux(const vlouver_esphome_position_t vLouver)
{
return vlouver_frontend_to_ac_louver_V(vLouver);
}
} // namespace aux_airconditioner
} // namespace esphome