1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 00:31:58 +00:00

[climate] Return StringRef from get_custom_fan_mode() and get_custom_preset() (#13103)

This commit is contained in:
J. Nick Koston
2026-01-11 17:53:06 -10:00
committed by GitHub
parent e1aac7601d
commit ea8ae2ae60
10 changed files with 81 additions and 33 deletions

View File

@@ -676,13 +676,13 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
if (!traits.get_supported_custom_fan_modes().empty() && climate->has_custom_fan_mode()) {
resp.custom_fan_mode = StringRef(climate->get_custom_fan_mode());
resp.custom_fan_mode = climate->get_custom_fan_mode();
}
if (traits.get_supports_presets() && climate->preset.has_value()) {
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
}
if (!traits.get_supported_custom_presets().empty() && climate->has_custom_preset()) {
resp.custom_preset = StringRef(climate->get_custom_preset());
resp.custom_preset = climate->get_custom_preset();
}
if (traits.get_supports_swing_modes())
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);

View File

@@ -164,21 +164,21 @@ void BedJetClimate::control(const ClimateCall &call) {
return;
}
} else if (call.has_custom_preset()) {
const char *preset = call.get_custom_preset();
auto preset = call.get_custom_preset();
bool result;
if (strcmp(preset, "M1") == 0) {
if (preset == "M1") {
result = this->parent_->button_memory1();
} else if (strcmp(preset, "M2") == 0) {
} else if (preset == "M2") {
result = this->parent_->button_memory2();
} else if (strcmp(preset, "M3") == 0) {
} else if (preset == "M3") {
result = this->parent_->button_memory3();
} else if (strcmp(preset, "LTD HT") == 0) {
} else if (preset == "LTD HT") {
result = this->parent_->button_heat();
} else if (strcmp(preset, "EXT HT") == 0) {
} else if (preset == "EXT HT") {
result = this->parent_->button_ext_heat();
} else {
ESP_LOGW(TAG, "Unsupported preset: %s", preset);
ESP_LOGW(TAG, "Unsupported preset: %.*s", (int) preset.size(), preset.c_str());
return;
}
@@ -208,10 +208,11 @@ void BedJetClimate::control(const ClimateCall &call) {
this->set_fan_mode_(fan_mode);
}
} else if (call.has_custom_fan_mode()) {
const char *fan_mode = call.get_custom_fan_mode();
auto fan_index = bedjet_fan_speed_to_step(fan_mode);
auto fan_mode = call.get_custom_fan_mode();
auto fan_index = bedjet_fan_speed_to_step(fan_mode.c_str());
if (fan_index <= 19) {
ESP_LOGV(TAG, "[%s] Converted fan mode %s to bedjet fan step %d", this->get_name().c_str(), fan_mode, fan_index);
ESP_LOGV(TAG, "[%s] Converted fan mode %.*s to bedjet fan step %d", this->get_name().c_str(),
(int) fan_mode.size(), fan_mode.c_str(), fan_index);
bool result = this->parent_->set_fan_index(fan_index);
if (result) {
this->set_custom_fan_mode_(fan_mode);

View File

@@ -682,19 +682,19 @@ bool Climate::set_fan_mode_(ClimateFanMode mode) {
return set_primary_mode(this->fan_mode, this->custom_fan_mode_, mode);
}
bool Climate::set_custom_fan_mode_(const char *mode) {
bool Climate::set_custom_fan_mode_(const char *mode, size_t len) {
auto traits = this->get_traits();
return set_custom_mode<ClimateFanMode>(this->custom_fan_mode_, this->fan_mode, traits.find_custom_fan_mode_(mode),
this->has_custom_fan_mode());
return set_custom_mode<ClimateFanMode>(this->custom_fan_mode_, this->fan_mode,
traits.find_custom_fan_mode_(mode, len), this->has_custom_fan_mode());
}
void Climate::clear_custom_fan_mode_() { this->custom_fan_mode_ = nullptr; }
bool Climate::set_preset_(ClimatePreset preset) { return set_primary_mode(this->preset, this->custom_preset_, preset); }
bool Climate::set_custom_preset_(const char *preset) {
bool Climate::set_custom_preset_(const char *preset, size_t len) {
auto traits = this->get_traits();
return set_custom_mode<ClimatePreset>(this->custom_preset_, this->preset, traits.find_custom_preset_(preset),
return set_custom_mode<ClimatePreset>(this->custom_preset_, this->preset, traits.find_custom_preset_(preset, len),
this->has_custom_preset());
}

View File

@@ -5,6 +5,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/preferences.h"
#include "esphome/core/string_ref.h"
#include "climate_mode.h"
#include "climate_traits.h"
@@ -110,8 +111,8 @@ class ClimateCall {
const optional<ClimateFanMode> &get_fan_mode() const;
const optional<ClimateSwingMode> &get_swing_mode() const;
const optional<ClimatePreset> &get_preset() const;
const char *get_custom_fan_mode() const { return this->custom_fan_mode_; }
const char *get_custom_preset() const { return this->custom_preset_; }
StringRef get_custom_fan_mode() const { return StringRef::from_maybe_nullptr(this->custom_fan_mode_); }
StringRef get_custom_preset() const { return StringRef::from_maybe_nullptr(this->custom_preset_); }
bool has_custom_fan_mode() const { return this->custom_fan_mode_ != nullptr; }
bool has_custom_preset() const { return this->custom_preset_ != nullptr; }
@@ -266,11 +267,11 @@ class Climate : public EntityBase {
/// The active swing mode of the climate device.
ClimateSwingMode swing_mode{CLIMATE_SWING_OFF};
/// Get the active custom fan mode (read-only access).
const char *get_custom_fan_mode() const { return this->custom_fan_mode_; }
/// Get the active custom fan mode (read-only access). Returns StringRef.
StringRef get_custom_fan_mode() const { return StringRef::from_maybe_nullptr(this->custom_fan_mode_); }
/// Get the active custom preset (read-only access).
const char *get_custom_preset() const { return this->custom_preset_; }
/// Get the active custom preset (read-only access). Returns StringRef.
StringRef get_custom_preset() const { return StringRef::from_maybe_nullptr(this->custom_preset_); }
protected:
friend ClimateCall;
@@ -280,7 +281,9 @@ class Climate : public EntityBase {
bool set_fan_mode_(ClimateFanMode mode);
/// Set custom fan mode. Reset primary fan mode. Return true if fan mode has been changed.
bool set_custom_fan_mode_(const char *mode);
bool set_custom_fan_mode_(const char *mode) { return this->set_custom_fan_mode_(mode, strlen(mode)); }
bool set_custom_fan_mode_(const char *mode, size_t len);
bool set_custom_fan_mode_(StringRef mode) { return this->set_custom_fan_mode_(mode.c_str(), mode.size()); }
/// Clear custom fan mode.
void clear_custom_fan_mode_();
@@ -288,7 +291,9 @@ class Climate : public EntityBase {
bool set_preset_(ClimatePreset preset);
/// Set custom preset. Reset primary preset. Return true if preset has been changed.
bool set_custom_preset_(const char *preset);
bool set_custom_preset_(const char *preset) { return this->set_custom_preset_(preset, strlen(preset)); }
bool set_custom_preset_(const char *preset, size_t len);
bool set_custom_preset_(StringRef preset) { return this->set_custom_preset_(preset.c_str(), preset.size()); }
/// Clear custom preset.
void clear_custom_preset_();

View File

@@ -65,12 +65,14 @@ void AirConditioner::control(const ClimateCall &call) {
if (call.get_preset().has_value()) {
ctrl.preset = Converters::to_midea_preset(call.get_preset().value());
} else if (call.has_custom_preset()) {
ctrl.preset = Converters::to_midea_preset(call.get_custom_preset());
// get_custom_preset() returns StringRef pointing to null-terminated string literals from codegen
ctrl.preset = Converters::to_midea_preset(call.get_custom_preset().c_str());
}
if (call.get_fan_mode().has_value()) {
ctrl.fanMode = Converters::to_midea_fan_mode(call.get_fan_mode().value());
} else if (call.has_custom_fan_mode()) {
ctrl.fanMode = Converters::to_midea_fan_mode(call.get_custom_fan_mode());
// get_custom_fan_mode() returns StringRef pointing to null-terminated string literals from codegen
ctrl.fanMode = Converters::to_midea_fan_mode(call.get_custom_fan_mode().c_str());
}
this->base_.control(ctrl);
}

View File

@@ -357,7 +357,7 @@ bool MQTTClimateComponent::publish_state_() {
}
}
if (this->device_->has_custom_preset())
payload = this->device_->get_custom_preset();
payload = this->device_->get_custom_preset().c_str();
if (!this->publish(this->get_preset_state_topic(), payload))
success = false;
}
@@ -429,7 +429,7 @@ bool MQTTClimateComponent::publish_state_() {
}
}
if (this->device_->has_custom_fan_mode())
payload = this->device_->get_custom_fan_mode();
payload = this->device_->get_custom_fan_mode().c_str();
if (!this->publish(this->get_fan_mode_state_topic(), payload))
success = false;
}

View File

@@ -1218,11 +1218,12 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
}
}
void ThermostatClimate::change_custom_preset_(const char *custom_preset) {
void ThermostatClimate::change_custom_preset_(const char *custom_preset, size_t len) {
// Linear search through custom preset configurations
const ThermostatClimateTargetTempConfig *config = nullptr;
for (const auto &entry : this->custom_preset_config_) {
if (strcmp(entry.name, custom_preset) == 0) {
// Compare first len chars, then verify entry.name ends there (same length)
if (strncmp(entry.name, custom_preset, len) == 0 && entry.name[len] == '\0') {
config = &entry.config;
break;
}
@@ -1231,7 +1232,7 @@ void ThermostatClimate::change_custom_preset_(const char *custom_preset) {
if (config != nullptr) {
ESP_LOGV(TAG, "Custom preset %s requested", custom_preset);
if (this->change_preset_internal_(*config) || !this->has_custom_preset() ||
strcmp(this->get_custom_preset(), custom_preset) != 0) {
this->get_custom_preset() != custom_preset) {
// Fire any preset changed trigger if defined
Trigger<> *trig = this->preset_change_trigger_;
// Use the base class method which handles pointer lookup and preset reset internally

View File

@@ -214,7 +214,13 @@ class ThermostatClimate : public climate::Climate, public Component {
/// Change to a provided preset setting; will reset temperature, mode, fan, and swing modes accordingly
void change_preset_(climate::ClimatePreset preset);
/// Change to a provided custom preset setting; will reset temperature, mode, fan, and swing modes accordingly
void change_custom_preset_(const char *custom_preset);
void change_custom_preset_(const char *custom_preset) {
this->change_custom_preset_(custom_preset, strlen(custom_preset));
}
void change_custom_preset_(const char *custom_preset, size_t len);
void change_custom_preset_(StringRef custom_preset) {
this->change_custom_preset_(custom_preset.c_str(), custom_preset.size());
}
/// Applies the temperature, mode, fan, and swing modes of the provided config.
/// This is agnostic of custom vs built in preset

View File

@@ -12,6 +12,25 @@ climate:
x.set_mode(CLIMATE_MODE_FAN_ONLY);
on_state:
- logger.log: State changed!
- lambda: |-
// Test get_custom_fan_mode() returns StringRef
if (id(midea_unit).has_custom_fan_mode()) {
auto fan_mode = id(midea_unit).get_custom_fan_mode();
// Compare with string literal using ==
if (fan_mode == "SILENT") {
ESP_LOGD("test", "Fan mode is SILENT");
}
// Log using %.*s format for StringRef
ESP_LOGD("test", "Custom fan mode: %.*s", (int) fan_mode.size(), fan_mode.c_str());
}
// Test get_custom_preset() returns StringRef
if (id(midea_unit).has_custom_preset()) {
auto preset = id(midea_unit).get_custom_preset();
// Check if empty
if (!preset.empty()) {
ESP_LOGD("test", "Custom preset: %.*s", (int) preset.size(), preset.c_str());
}
}
transmitter_id: xmitr
period: 1s
num_attempts: 5

View File

@@ -5,6 +5,7 @@ sensor:
climate:
- platform: thermostat
id: test_thermostat
name: Test Thermostat
sensor: thermostat_sensor
humidity_sensor: thermostat_sensor
@@ -15,6 +16,19 @@ climate:
- name: Away
default_target_temperature_low: 16°C
default_target_temperature_high: 20°C
on_state:
- lambda: |-
// Test get_custom_preset() returns std::string_view
// "Default Preset" is a custom preset (not a standard ClimatePreset name)
if (id(test_thermostat).has_custom_preset()) {
auto preset = id(test_thermostat).get_custom_preset();
// Compare with string literal using ==
if (preset == "Default Preset") {
ESP_LOGD("test", "Preset is Default Preset");
}
// Log using %.*s format for StringRef
ESP_LOGD("test", "Custom preset: %.*s", (int) preset.size(), preset.c_str());
}
idle_action:
- logger.log: idle_action
cool_action: