mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	[fan] Use std::vector for preset modes, preserve config order (#11483)
This commit is contained in:
		@@ -425,7 +425,7 @@ message ListEntitiesFanResponse {
 | 
			
		||||
  bool disabled_by_default = 9;
 | 
			
		||||
  string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
 | 
			
		||||
  EntityCategory entity_category = 11;
 | 
			
		||||
  repeated string supported_preset_modes = 12 [(container_pointer) = "std::set"];
 | 
			
		||||
  repeated string supported_preset_modes = 12 [(container_pointer_no_template) = "std::vector<const char *>"];
 | 
			
		||||
  uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
 | 
			
		||||
}
 | 
			
		||||
// Deprecated in API version 1.6 - only used in deprecated fields
 | 
			
		||||
 
 | 
			
		||||
@@ -423,7 +423,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
 | 
			
		||||
  msg.supports_speed = traits.supports_speed();
 | 
			
		||||
  msg.supports_direction = traits.supports_direction();
 | 
			
		||||
  msg.supported_speed_count = traits.supported_speed_count();
 | 
			
		||||
  msg.supported_preset_modes = &traits.supported_preset_modes_for_api_();
 | 
			
		||||
  msg.supported_preset_modes = &traits.supported_preset_modes();
 | 
			
		||||
  return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
 | 
			
		||||
 
 | 
			
		||||
@@ -355,8 +355,8 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(10, this->icon_ref_);
 | 
			
		||||
#endif
 | 
			
		||||
  buffer.encode_uint32(11, static_cast<uint32_t>(this->entity_category));
 | 
			
		||||
  for (const auto &it : *this->supported_preset_modes) {
 | 
			
		||||
    buffer.encode_string(12, it, true);
 | 
			
		||||
  for (const char *it : *this->supported_preset_modes) {
 | 
			
		||||
    buffer.encode_string(12, it, strlen(it), true);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_DEVICES
 | 
			
		||||
  buffer.encode_uint32(13, this->device_id);
 | 
			
		||||
@@ -376,8 +376,8 @@ void ListEntitiesFanResponse::calculate_size(ProtoSize &size) const {
 | 
			
		||||
#endif
 | 
			
		||||
  size.add_uint32(1, static_cast<uint32_t>(this->entity_category));
 | 
			
		||||
  if (!this->supported_preset_modes->empty()) {
 | 
			
		||||
    for (const auto &it : *this->supported_preset_modes) {
 | 
			
		||||
      size.add_length_force(1, it.size());
 | 
			
		||||
    for (const char *it : *this->supported_preset_modes) {
 | 
			
		||||
      size.add_length_force(1, strlen(it));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_DEVICES
 | 
			
		||||
 
 | 
			
		||||
@@ -725,7 +725,7 @@ class ListEntitiesFanResponse final : public InfoResponseProtoMessage {
 | 
			
		||||
  bool supports_speed{false};
 | 
			
		||||
  bool supports_direction{false};
 | 
			
		||||
  int32_t supported_speed_count{0};
 | 
			
		||||
  const std::set<std::string> *supported_preset_modes{};
 | 
			
		||||
  const std::vector<const char *> *supported_preset_modes{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void calculate_size(ProtoSize &size) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,14 @@ void FanCall::validate_() {
 | 
			
		||||
 | 
			
		||||
  if (!this->preset_mode_.empty()) {
 | 
			
		||||
    const auto &preset_modes = traits.supported_preset_modes();
 | 
			
		||||
    if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
 | 
			
		||||
    bool found = false;
 | 
			
		||||
    for (const auto &mode : preset_modes) {
 | 
			
		||||
      if (strcmp(mode, this->preset_mode_.c_str()) == 0) {
 | 
			
		||||
        found = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!found) {
 | 
			
		||||
      ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str());
 | 
			
		||||
      this->preset_mode_.clear();
 | 
			
		||||
    }
 | 
			
		||||
@@ -92,11 +99,12 @@ FanCall FanRestoreState::to_call(Fan &fan) {
 | 
			
		||||
  call.set_speed(this->speed);
 | 
			
		||||
  call.set_direction(this->direction);
 | 
			
		||||
 | 
			
		||||
  if (fan.get_traits().supports_preset_modes()) {
 | 
			
		||||
  auto traits = fan.get_traits();
 | 
			
		||||
  if (traits.supports_preset_modes()) {
 | 
			
		||||
    // Use stored preset index to get preset name
 | 
			
		||||
    const auto &preset_modes = fan.get_traits().supported_preset_modes();
 | 
			
		||||
    const auto &preset_modes = traits.supported_preset_modes();
 | 
			
		||||
    if (this->preset_mode < preset_modes.size()) {
 | 
			
		||||
      call.set_preset_mode(*std::next(preset_modes.begin(), this->preset_mode));
 | 
			
		||||
      call.set_preset_mode(preset_modes[this->preset_mode]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return call;
 | 
			
		||||
@@ -107,11 +115,12 @@ void FanRestoreState::apply(Fan &fan) {
 | 
			
		||||
  fan.speed = this->speed;
 | 
			
		||||
  fan.direction = this->direction;
 | 
			
		||||
 | 
			
		||||
  if (fan.get_traits().supports_preset_modes()) {
 | 
			
		||||
  auto traits = fan.get_traits();
 | 
			
		||||
  if (traits.supports_preset_modes()) {
 | 
			
		||||
    // Use stored preset index to get preset name
 | 
			
		||||
    const auto &preset_modes = fan.get_traits().supported_preset_modes();
 | 
			
		||||
    const auto &preset_modes = traits.supported_preset_modes();
 | 
			
		||||
    if (this->preset_mode < preset_modes.size()) {
 | 
			
		||||
      fan.preset_mode = *std::next(preset_modes.begin(), this->preset_mode);
 | 
			
		||||
      fan.preset_mode = preset_modes[this->preset_mode];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  fan.publish_state();
 | 
			
		||||
@@ -182,18 +191,25 @@ void Fan::save_state_() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto traits = this->get_traits();
 | 
			
		||||
 | 
			
		||||
  FanRestoreState state{};
 | 
			
		||||
  state.state = this->state;
 | 
			
		||||
  state.oscillating = this->oscillating;
 | 
			
		||||
  state.speed = this->speed;
 | 
			
		||||
  state.direction = this->direction;
 | 
			
		||||
 | 
			
		||||
  if (this->get_traits().supports_preset_modes() && !this->preset_mode.empty()) {
 | 
			
		||||
    const auto &preset_modes = this->get_traits().supported_preset_modes();
 | 
			
		||||
  if (traits.supports_preset_modes() && !this->preset_mode.empty()) {
 | 
			
		||||
    const auto &preset_modes = traits.supported_preset_modes();
 | 
			
		||||
    // Store index of current preset mode
 | 
			
		||||
    auto preset_iterator = preset_modes.find(this->preset_mode);
 | 
			
		||||
    if (preset_iterator != preset_modes.end())
 | 
			
		||||
      state.preset_mode = std::distance(preset_modes.begin(), preset_iterator);
 | 
			
		||||
    size_t i = 0;
 | 
			
		||||
    for (const auto &mode : preset_modes) {
 | 
			
		||||
      if (strcmp(mode, this->preset_mode.c_str()) == 0) {
 | 
			
		||||
        state.preset_mode = i;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      i++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->rtc_.save(&state);
 | 
			
		||||
@@ -216,8 +232,8 @@ void Fan::dump_traits_(const char *tag, const char *prefix) {
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.supports_preset_modes()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "%s  Supported presets:", prefix);
 | 
			
		||||
    for (const std::string &s : traits.supported_preset_modes())
 | 
			
		||||
      ESP_LOGCONFIG(tag, "%s    - %s", prefix, s.c_str());
 | 
			
		||||
    for (const char *s : traits.supported_preset_modes())
 | 
			
		||||
      ESP_LOGCONFIG(tag, "%s    - %s", prefix, s);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,9 @@
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <initializer_list>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
namespace api {
 | 
			
		||||
class APIConnection;
 | 
			
		||||
}  // namespace api
 | 
			
		||||
#endif
 | 
			
		||||
namespace esphome {
 | 
			
		||||
 | 
			
		||||
namespace fan {
 | 
			
		||||
 | 
			
		||||
@@ -36,27 +30,27 @@ class FanTraits {
 | 
			
		||||
  /// Set whether this fan supports changing direction
 | 
			
		||||
  void set_direction(bool direction) { this->direction_ = direction; }
 | 
			
		||||
  /// Return the preset modes supported by the fan.
 | 
			
		||||
  std::set<std::string> supported_preset_modes() const { return this->preset_modes_; }
 | 
			
		||||
  /// Set the preset modes supported by the fan.
 | 
			
		||||
  void set_supported_preset_modes(const std::set<std::string> &preset_modes) { this->preset_modes_ = preset_modes; }
 | 
			
		||||
  const std::vector<const char *> &supported_preset_modes() const { return this->preset_modes_; }
 | 
			
		||||
  /// Set the preset modes supported by the fan (from initializer list).
 | 
			
		||||
  void set_supported_preset_modes(std::initializer_list<const char *> preset_modes) {
 | 
			
		||||
    this->preset_modes_ = preset_modes;
 | 
			
		||||
  }
 | 
			
		||||
  /// Set the preset modes supported by the fan (from vector).
 | 
			
		||||
  void set_supported_preset_modes(const std::vector<const char *> &preset_modes) { this->preset_modes_ = preset_modes; }
 | 
			
		||||
 | 
			
		||||
  // Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
 | 
			
		||||
  void set_supported_preset_modes(const std::vector<std::string> &preset_modes) = delete;
 | 
			
		||||
  void set_supported_preset_modes(std::initializer_list<std::string> preset_modes) = delete;
 | 
			
		||||
 | 
			
		||||
  /// Return if preset modes are supported
 | 
			
		||||
  bool supports_preset_modes() const { return !this->preset_modes_.empty(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
#ifdef USE_API
 | 
			
		||||
  // The API connection is a friend class to access internal methods
 | 
			
		||||
  friend class api::APIConnection;
 | 
			
		||||
  // This method returns a reference to the internal preset modes set.
 | 
			
		||||
  // It is used by the API to avoid copying data when encoding messages.
 | 
			
		||||
  // Warning: Do not use this method outside of the API connection code.
 | 
			
		||||
  // It returns a reference to internal data that can be invalidated.
 | 
			
		||||
  const std::set<std::string> &supported_preset_modes_for_api_() const { return this->preset_modes_; }
 | 
			
		||||
#endif
 | 
			
		||||
  bool oscillation_{false};
 | 
			
		||||
  bool speed_{false};
 | 
			
		||||
  bool direction_{false};
 | 
			
		||||
  int speed_count_{};
 | 
			
		||||
  std::set<std::string> preset_modes_{};
 | 
			
		||||
  std::vector<const char *> preset_modes_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace fan
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/components/output/binary_output.h"
 | 
			
		||||
#include "esphome/components/output/float_output.h"
 | 
			
		||||
@@ -22,7 +20,7 @@ class HBridgeFan : public Component, public fan::Fan {
 | 
			
		||||
  void set_pin_a(output::FloatOutput *pin_a) { pin_a_ = pin_a; }
 | 
			
		||||
  void set_pin_b(output::FloatOutput *pin_b) { pin_b_ = pin_b; }
 | 
			
		||||
  void set_enable_pin(output::FloatOutput *enable) { enable_ = enable; }
 | 
			
		||||
  void set_preset_modes(const std::set<std::string> &presets) { preset_modes_ = presets; }
 | 
			
		||||
  void set_preset_modes(std::initializer_list<const char *> presets) { preset_modes_ = presets; }
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
@@ -38,7 +36,7 @@ class HBridgeFan : public Component, public fan::Fan {
 | 
			
		||||
  int speed_count_{};
 | 
			
		||||
  DecayMode decay_mode_{DECAY_MODE_SLOW};
 | 
			
		||||
  fan::FanTraits traits_;
 | 
			
		||||
  std::set<std::string> preset_modes_{};
 | 
			
		||||
  std::vector<const char *> preset_modes_{};
 | 
			
		||||
 | 
			
		||||
  void control(const fan::FanCall &call) override;
 | 
			
		||||
  void write_state_();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/output/binary_output.h"
 | 
			
		||||
#include "esphome/components/output/float_output.h"
 | 
			
		||||
@@ -18,7 +16,7 @@ class SpeedFan : public Component, public fan::Fan {
 | 
			
		||||
  void set_output(output::FloatOutput *output) { this->output_ = output; }
 | 
			
		||||
  void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
 | 
			
		||||
  void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
 | 
			
		||||
  void set_preset_modes(const std::set<std::string> &presets) { this->preset_modes_ = presets; }
 | 
			
		||||
  void set_preset_modes(std::initializer_list<const char *> presets) { this->preset_modes_ = presets; }
 | 
			
		||||
  fan::FanTraits get_traits() override { return this->traits_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
@@ -30,7 +28,7 @@ class SpeedFan : public Component, public fan::Fan {
 | 
			
		||||
  output::BinaryOutput *direction_{nullptr};
 | 
			
		||||
  int speed_count_{};
 | 
			
		||||
  fan::FanTraits traits_;
 | 
			
		||||
  std::set<std::string> preset_modes_{};
 | 
			
		||||
  std::vector<const char *> preset_modes_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace speed
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/fan/fan.h"
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +14,7 @@ class TemplateFan : public Component, public fan::Fan {
 | 
			
		||||
  void set_has_direction(bool has_direction) { this->has_direction_ = has_direction; }
 | 
			
		||||
  void set_has_oscillating(bool has_oscillating) { this->has_oscillating_ = has_oscillating; }
 | 
			
		||||
  void set_speed_count(int count) { this->speed_count_ = count; }
 | 
			
		||||
  void set_preset_modes(const std::set<std::string> &presets) { this->preset_modes_ = presets; }
 | 
			
		||||
  void set_preset_modes(std::initializer_list<const char *> presets) { this->preset_modes_ = presets; }
 | 
			
		||||
  fan::FanTraits get_traits() override { return this->traits_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
@@ -26,7 +24,7 @@ class TemplateFan : public Component, public fan::Fan {
 | 
			
		||||
  bool has_direction_{false};
 | 
			
		||||
  int speed_count_{0};
 | 
			
		||||
  fan::FanTraits traits_;
 | 
			
		||||
  std::set<std::string> preset_modes_{};
 | 
			
		||||
  std::vector<const char *> preset_modes_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace template_
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user