mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 23:21:54 +00:00 
			
		
		
		
	Merge branch 'select_options' into integration
This commit is contained in:
		| @@ -207,14 +207,14 @@ def choose_upload_log_host( | ||||
|                     if has_mqtt_logging(): | ||||
|                         resolved.append("MQTT") | ||||
|  | ||||
|                     if has_api() and has_non_ip_address(): | ||||
|                     if has_api() and has_non_ip_address() and has_resolvable_address(): | ||||
|                         resolved.extend(_resolve_with_cache(CORE.address, purpose)) | ||||
|  | ||||
|                 elif purpose == Purpose.UPLOADING: | ||||
|                     if has_ota() and has_mqtt_ip_lookup(): | ||||
|                         resolved.append("MQTTIP") | ||||
|  | ||||
|                     if has_ota() and has_non_ip_address(): | ||||
|                     if has_ota() and has_non_ip_address() and has_resolvable_address(): | ||||
|                         resolved.extend(_resolve_with_cache(CORE.address, purpose)) | ||||
|             else: | ||||
|                 resolved.append(device) | ||||
| @@ -318,7 +318,17 @@ def has_resolvable_address() -> bool: | ||||
|     """Check if CORE.address is resolvable (via mDNS, DNS, or is an IP address).""" | ||||
|     # Any address (IP, mDNS hostname, or regular DNS hostname) is resolvable | ||||
|     # The resolve_ip_address() function in helpers.py handles all types via AsyncResolver | ||||
|     return CORE.address is not None | ||||
|     if CORE.address is None: | ||||
|         return False | ||||
|  | ||||
|     if has_ip_address(): | ||||
|         return True | ||||
|  | ||||
|     if has_mdns(): | ||||
|         return True | ||||
|  | ||||
|     # .local mDNS hostnames are only resolvable if mDNS is enabled | ||||
|     return not CORE.address.endswith(".local") | ||||
|  | ||||
|  | ||||
| def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str): | ||||
|   | ||||
| @@ -877,7 +877,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection | ||||
|                                               bool is_single) { | ||||
|   auto *select = static_cast<select::Select *>(entity); | ||||
|   SelectStateResponse resp; | ||||
|   resp.set_state(StringRef(select->state)); | ||||
|   resp.set_state(StringRef(select->current_option())); | ||||
|   resp.missing_state = !select->has_state(); | ||||
|   return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); | ||||
| } | ||||
|   | ||||
| @@ -7,19 +7,19 @@ namespace copy { | ||||
| static const char *const TAG = "copy.select"; | ||||
|  | ||||
| void CopySelect::setup() { | ||||
|   source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(value); }); | ||||
|   source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(index); }); | ||||
|  | ||||
|   traits.set_options(source_->traits.get_options()); | ||||
|  | ||||
|   if (source_->has_state()) | ||||
|     this->publish_state(source_->state); | ||||
|     this->publish_state(source_->active_index().value()); | ||||
| } | ||||
|  | ||||
| void CopySelect::dump_config() { LOG_SELECT("", "Copy Select", this); } | ||||
|  | ||||
| void CopySelect::control(const std::string &value) { | ||||
| void CopySelect::control(size_t index) { | ||||
|   auto call = source_->make_call(); | ||||
|   call.set_option(value); | ||||
|   call.set_index(index); | ||||
|   call.perform(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ class CopySelect : public select::Select, public Component { | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
|  | ||||
|   select::Select *source_; | ||||
| }; | ||||
|   | ||||
| @@ -42,7 +42,7 @@ std::string MenuItemSelect::get_value_text() const { | ||||
|     result = this->value_getter_.value()(this); | ||||
|   } else { | ||||
|     if (this->select_var_ != nullptr) { | ||||
|       result = this->select_var_->state; | ||||
|       result = this->select_var_->current_option(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -121,9 +121,9 @@ constexpr Uint8ToString OUT_PIN_LEVELS_BY_UINT[] = { | ||||
| }; | ||||
|  | ||||
| // Helper functions for lookups | ||||
| template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) { | ||||
| template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const char *str) { | ||||
|   for (const auto &entry : arr) { | ||||
|     if (str == entry.str) | ||||
|     if (strcmp(str, entry.str) == 0) | ||||
|       return entry.value; | ||||
|   } | ||||
|   return 0xFF;  // Not found | ||||
| @@ -441,7 +441,7 @@ bool LD2410Component::handle_ack_data_() { | ||||
|       ESP_LOGV(TAG, "Baud rate change"); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->baud_rate_select_ != nullptr) { | ||||
|         ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); | ||||
|         ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option()); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
| @@ -626,14 +626,14 @@ void LD2410Component::set_bluetooth(bool enable) { | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_distance_resolution(const std::string &state) { | ||||
| void LD2410Component::set_distance_resolution(const char *state) { | ||||
|   this->set_config_mode_(true); | ||||
|   const uint8_t cmd_value[2] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00}; | ||||
|   this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value)); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_baud_rate(const std::string &state) { | ||||
| void LD2410Component::set_baud_rate(const char *state) { | ||||
|   this->set_config_mode_(true); | ||||
|   const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; | ||||
|   this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); | ||||
| @@ -759,10 +759,10 @@ void LD2410Component::set_light_out_control() { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { | ||||
|     this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->state); | ||||
|     this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option()); | ||||
|   } | ||||
|   if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) { | ||||
|     this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->state); | ||||
|     this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option()); | ||||
|   } | ||||
| #endif | ||||
|   this->set_config_mode_(true); | ||||
|   | ||||
| @@ -98,8 +98,8 @@ class LD2410Component : public Component, public uart::UARTDevice { | ||||
|   void read_all_info(); | ||||
|   void restart_and_read_all_info(); | ||||
|   void set_bluetooth(bool enable); | ||||
|   void set_distance_resolution(const std::string &state); | ||||
|   void set_baud_rate(const std::string &state); | ||||
|   void set_distance_resolution(const char *state); | ||||
|   void set_baud_rate(const char *state); | ||||
|   void factory_reset(); | ||||
|  | ||||
|  protected: | ||||
|   | ||||
| @@ -3,9 +3,9 @@ | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void BaudRateSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_baud_rate(state); | ||||
| void BaudRateSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_baud_rate(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class BaudRateSelect : public select::Select, public Parented<LD2410Component> { | ||||
|   BaudRateSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
| @@ -3,9 +3,9 @@ | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void DistanceResolutionSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_distance_resolution(state); | ||||
| void DistanceResolutionSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_distance_resolution(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class DistanceResolutionSelect : public select::Select, public Parented<LD2410Co | ||||
|   DistanceResolutionSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void LightOutControlSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
| void LightOutControlSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_light_out_control(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class LightOutControlSelect : public select::Select, public Parented<LD2410Compo | ||||
|   LightOutControlSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
| @@ -132,9 +132,9 @@ constexpr Uint8ToString OUT_PIN_LEVELS_BY_UINT[] = { | ||||
| }; | ||||
|  | ||||
| // Helper functions for lookups | ||||
| template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) { | ||||
| template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const char *str) { | ||||
|   for (const auto &entry : arr) { | ||||
|     if (str == entry.str) { | ||||
|     if (strcmp(str, entry.str) == 0) { | ||||
|       return entry.value; | ||||
|     } | ||||
|   } | ||||
| @@ -485,7 +485,7 @@ bool LD2412Component::handle_ack_data_() { | ||||
|       ESP_LOGV(TAG, "Baud rate change"); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->baud_rate_select_ != nullptr) { | ||||
|         ESP_LOGW(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); | ||||
|         ESP_LOGW(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option()); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
| @@ -699,14 +699,14 @@ void LD2412Component::set_bluetooth(bool enable) { | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2412Component::set_distance_resolution(const std::string &state) { | ||||
| void LD2412Component::set_distance_resolution(const char *state) { | ||||
|   this->set_config_mode_(true); | ||||
|   const uint8_t cmd_value[6] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||
|   this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value)); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2412Component::set_baud_rate(const std::string &state) { | ||||
| void LD2412Component::set_baud_rate(const char *state) { | ||||
|   this->set_config_mode_(true); | ||||
|   const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; | ||||
|   this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); | ||||
| @@ -783,7 +783,7 @@ void LD2412Component::set_basic_config() { | ||||
|       1,    TOTAL_GATES, DEFAULT_PRESENCE_TIMEOUT, 0, | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|       find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->state), | ||||
|       find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option()), | ||||
| #else | ||||
|       0x01,  // Default value if not using select | ||||
| #endif | ||||
| @@ -837,7 +837,7 @@ void LD2412Component::set_light_out_control() { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { | ||||
|     this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->state); | ||||
|     this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option()); | ||||
|   } | ||||
| #endif | ||||
|   uint8_t value[2] = {this->light_function_, this->light_threshold_}; | ||||
|   | ||||
| @@ -99,8 +99,8 @@ class LD2412Component : public Component, public uart::UARTDevice { | ||||
|   void read_all_info(); | ||||
|   void restart_and_read_all_info(); | ||||
|   void set_bluetooth(bool enable); | ||||
|   void set_distance_resolution(const std::string &state); | ||||
|   void set_baud_rate(const std::string &state); | ||||
|   void set_distance_resolution(const char *state); | ||||
|   void set_baud_rate(const char *state); | ||||
|   void factory_reset(); | ||||
|   void start_dynamic_background_correction(); | ||||
|  | ||||
|   | ||||
| @@ -3,9 +3,9 @@ | ||||
| namespace esphome { | ||||
| namespace ld2412 { | ||||
|  | ||||
| void BaudRateSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_baud_rate(state); | ||||
| void BaudRateSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_baud_rate(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2412 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class BaudRateSelect : public select::Select, public Parented<LD2412Component> { | ||||
|   BaudRateSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2412 | ||||
|   | ||||
| @@ -3,9 +3,9 @@ | ||||
| namespace esphome { | ||||
| namespace ld2412 { | ||||
|  | ||||
| void DistanceResolutionSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_distance_resolution(state); | ||||
| void DistanceResolutionSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_distance_resolution(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2412 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class DistanceResolutionSelect : public select::Select, public Parented<LD2412Co | ||||
|   DistanceResolutionSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2412 | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
| namespace esphome { | ||||
| namespace ld2412 { | ||||
|  | ||||
| void LightOutControlSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
| void LightOutControlSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_light_out_control(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class LightOutControlSelect : public select::Select, public Parented<LD2412Compo | ||||
|   LightOutControlSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2412 | ||||
|   | ||||
| @@ -379,7 +379,7 @@ void LD2420Component::report_gate_data() { | ||||
|   ESP_LOGI(TAG, "Total samples: %d", this->total_sample_number_counter); | ||||
| } | ||||
|  | ||||
| void LD2420Component::set_operating_mode(const std::string &state) { | ||||
| void LD2420Component::set_operating_mode(const char *state) { | ||||
|   // If unsupported firmware ignore mode select | ||||
|   if (ld2420::get_firmware_int(firmware_ver_) >= CALIBRATE_VERSION_MIN) { | ||||
|     this->current_operating_mode = find_uint8(OP_MODE_BY_STR, state); | ||||
|   | ||||
| @@ -107,7 +107,7 @@ class LD2420Component : public Component, public uart::UARTDevice { | ||||
|   int send_cmd_from_array(CmdFrameT cmd_frame); | ||||
|   void report_gate_data(); | ||||
|   void handle_cmd_error(uint8_t error); | ||||
|   void set_operating_mode(const std::string &state); | ||||
|   void set_operating_mode(const char *state); | ||||
|   void auto_calibrate_sensitivity(); | ||||
|   void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number); | ||||
|   uint8_t set_config_mode(bool enable); | ||||
|   | ||||
| @@ -7,9 +7,9 @@ namespace ld2420 { | ||||
|  | ||||
| static const char *const TAG = "ld2420.select"; | ||||
|  | ||||
| void LD2420Select::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_operating_mode(value); | ||||
| void LD2420Select::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_operating_mode(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2420 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class LD2420Select : public Component, public select::Select, public Parented<LD | ||||
|   LD2420Select() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2420 | ||||
|   | ||||
| @@ -380,7 +380,7 @@ void LD2450Component::read_all_info() { | ||||
|   this->set_config_mode_(false); | ||||
| #ifdef USE_SELECT | ||||
|   const auto baud_rate = std::to_string(this->parent_->get_baud_rate()); | ||||
|   if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) { | ||||
|   if (this->baud_rate_select_ != nullptr && strcmp(this->baud_rate_select_->current_option(), baud_rate.c_str()) != 0) { | ||||
|     this->baud_rate_select_->publish_state(baud_rate); | ||||
|   } | ||||
|   this->publish_zone_type(); | ||||
| @@ -635,7 +635,7 @@ bool LD2450Component::handle_ack_data_() { | ||||
|       ESP_LOGV(TAG, "Baud rate change"); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->baud_rate_select_ != nullptr) { | ||||
|         ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); | ||||
|         ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option()); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
| @@ -716,7 +716,7 @@ bool LD2450Component::handle_ack_data_() { | ||||
|       this->publish_zone_type(); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->zone_type_select_ != nullptr) { | ||||
|         ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->state.c_str()); | ||||
|         ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->current_option()); | ||||
|       } | ||||
| #endif | ||||
|       if (this->buffer_data_[10] == 0x00) { | ||||
| @@ -790,7 +790,7 @@ void LD2450Component::set_bluetooth(bool enable) { | ||||
| } | ||||
|  | ||||
| // Set Baud rate | ||||
| void LD2450Component::set_baud_rate(const std::string &state) { | ||||
| void LD2450Component::set_baud_rate(const char *state) { | ||||
|   this->set_config_mode_(true); | ||||
|   const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; | ||||
|   this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value)); | ||||
| @@ -798,8 +798,8 @@ void LD2450Component::set_baud_rate(const std::string &state) { | ||||
| } | ||||
|  | ||||
| // Set Zone Type - one of: Disabled, Detection, Filter | ||||
| void LD2450Component::set_zone_type(const std::string &state) { | ||||
|   ESP_LOGV(TAG, "Set zone type: %s", state.c_str()); | ||||
| void LD2450Component::set_zone_type(const char *state) { | ||||
|   ESP_LOGV(TAG, "Set zone type: %s", state); | ||||
|   uint8_t zone_type = find_uint8(ZONE_TYPE_BY_STR, state); | ||||
|   this->zone_type_ = zone_type; | ||||
|   this->send_set_zone_command_(); | ||||
|   | ||||
| @@ -115,8 +115,8 @@ class LD2450Component : public Component, public uart::UARTDevice { | ||||
|   void restart_and_read_all_info(); | ||||
|   void set_bluetooth(bool enable); | ||||
|   void set_multi_target(bool enable); | ||||
|   void set_baud_rate(const std::string &state); | ||||
|   void set_zone_type(const std::string &state); | ||||
|   void set_baud_rate(const char *state); | ||||
|   void set_zone_type(const char *state); | ||||
|   void publish_zone_type(); | ||||
|   void factory_reset(); | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|   | ||||
| @@ -3,9 +3,9 @@ | ||||
| namespace esphome { | ||||
| namespace ld2450 { | ||||
|  | ||||
| void BaudRateSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_baud_rate(state); | ||||
| void BaudRateSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_baud_rate(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2450 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class BaudRateSelect : public select::Select, public Parented<LD2450Component> { | ||||
|   BaudRateSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2450 | ||||
|   | ||||
| @@ -3,9 +3,9 @@ | ||||
| namespace esphome { | ||||
| namespace ld2450 { | ||||
|  | ||||
| void ZoneTypeSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_zone_type(state); | ||||
| void ZoneTypeSelect::control(size_t index) { | ||||
|   this->publish_state(index); | ||||
|   this->parent_->set_zone_type(this->option_at(index)); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2450 | ||||
|   | ||||
| @@ -11,7 +11,7 @@ class ZoneTypeSelect : public select::Select, public Parented<LD2450Component> { | ||||
|   ZoneTypeSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2450 | ||||
|   | ||||
| @@ -3,10 +3,10 @@ | ||||
| namespace esphome::logger { | ||||
|  | ||||
| void LoggerLevelSelect::publish_state(int level) { | ||||
|   const auto &option = this->at(level_to_index(level)); | ||||
|   if (!option) | ||||
|   auto index = level_to_index(level); | ||||
|   if (!this->has_index(index)) | ||||
|     return; | ||||
|   Select::publish_state(option.value()); | ||||
|   Select::publish_state(index); | ||||
| } | ||||
|  | ||||
| void LoggerLevelSelect::setup() { | ||||
| @@ -14,11 +14,6 @@ void LoggerLevelSelect::setup() { | ||||
|   this->publish_state(this->parent_->get_log_level()); | ||||
| } | ||||
|  | ||||
| void LoggerLevelSelect::control(const std::string &value) { | ||||
|   const auto index = this->index_of(value); | ||||
|   if (!index) | ||||
|     return; | ||||
|   this->parent_->set_log_level(index_to_level(index.value())); | ||||
| } | ||||
| void LoggerLevelSelect::control(size_t index) { this->parent_->set_log_level(index_to_level(index)); } | ||||
|  | ||||
| }  // namespace esphome::logger | ||||
|   | ||||
| @@ -9,7 +9,7 @@ class LoggerLevelSelect : public Component, public select::Select, public Parent | ||||
|  public: | ||||
|   void publish_state(int level); | ||||
|   void setup() override; | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
|  | ||||
|  protected: | ||||
|   // Convert log level to option index (skip CONFIG at level 4) | ||||
|   | ||||
| @@ -21,7 +21,8 @@ void MQTTSelectComponent::setup() { | ||||
|     call.set_option(state); | ||||
|     call.perform(); | ||||
|   }); | ||||
|   this->select_->add_on_state_callback([this](const std::string &state, size_t index) { this->publish_state(state); }); | ||||
|   this->select_->add_on_state_callback( | ||||
|       [this](const std::string &state, size_t index) { this->publish_state(this->select_->option_at(index)); }); | ||||
| } | ||||
|  | ||||
| void MQTTSelectComponent::dump_config() { | ||||
| @@ -44,7 +45,7 @@ void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon | ||||
| } | ||||
| bool MQTTSelectComponent::send_initial_state() { | ||||
|   if (this->select_->has_state()) { | ||||
|     return this->publish_state(this->select_->state); | ||||
|     return this->publish_state(this->select_->current_option()); | ||||
|   } else { | ||||
|     return true; | ||||
|   } | ||||
|   | ||||
| @@ -435,12 +435,12 @@ void MR24HPC1Component::r24_frame_parse_open_underlying_information_(uint8_t *da | ||||
|   } else if ((this->existence_boundary_select_ != nullptr) && | ||||
|              ((data[FRAME_COMMAND_WORD_INDEX] == 0x0a) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8a))) { | ||||
|     if (this->existence_boundary_select_->has_index(data[FRAME_DATA_INDEX] - 1)) { | ||||
|       this->existence_boundary_select_->publish_state(S_BOUNDARY_STR[data[FRAME_DATA_INDEX] - 1]); | ||||
|       this->existence_boundary_select_->publish_state(data[FRAME_DATA_INDEX] - 1); | ||||
|     } | ||||
|   } else if ((this->motion_boundary_select_ != nullptr) && | ||||
|              ((data[FRAME_COMMAND_WORD_INDEX] == 0x0b) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8b))) { | ||||
|     if (this->motion_boundary_select_->has_index(data[FRAME_DATA_INDEX] - 1)) { | ||||
|       this->motion_boundary_select_->publish_state(S_BOUNDARY_STR[data[FRAME_DATA_INDEX] - 1]); | ||||
|       this->motion_boundary_select_->publish_state(data[FRAME_DATA_INDEX] - 1); | ||||
|     } | ||||
|   } else if ((this->motion_trigger_number_ != nullptr) && | ||||
|              ((data[FRAME_COMMAND_WORD_INDEX] == 0x0c) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8c))) { | ||||
| @@ -515,7 +515,7 @@ void MR24HPC1Component::r24_frame_parse_work_status_(uint8_t *data) { | ||||
|     ESP_LOGD(TAG, "Reply: get radar init status 0x%02X", data[FRAME_DATA_INDEX]); | ||||
|   } else if (data[FRAME_COMMAND_WORD_INDEX] == 0x07) { | ||||
|     if ((this->scene_mode_select_ != nullptr) && (this->scene_mode_select_->has_index(data[FRAME_DATA_INDEX]))) { | ||||
|       this->scene_mode_select_->publish_state(S_SCENE_STR[data[FRAME_DATA_INDEX]]); | ||||
|       this->scene_mode_select_->publish_state(data[FRAME_DATA_INDEX]); | ||||
|     } else { | ||||
|       ESP_LOGD(TAG, "Select has index offset %d Error", data[FRAME_DATA_INDEX]); | ||||
|     } | ||||
| @@ -538,7 +538,7 @@ void MR24HPC1Component::r24_frame_parse_work_status_(uint8_t *data) { | ||||
|     ESP_LOGD(TAG, "Reply: get radar init status 0x%02X", data[FRAME_DATA_INDEX]); | ||||
|   } else if (data[FRAME_COMMAND_WORD_INDEX] == 0x87) { | ||||
|     if ((this->scene_mode_select_ != nullptr) && (this->scene_mode_select_->has_index(data[FRAME_DATA_INDEX]))) { | ||||
|       this->scene_mode_select_->publish_state(S_SCENE_STR[data[FRAME_DATA_INDEX]]); | ||||
|       this->scene_mode_select_->publish_state(data[FRAME_DATA_INDEX]); | ||||
|     } else { | ||||
|       ESP_LOGD(TAG, "Select has index offset %d Error", data[FRAME_DATA_INDEX]); | ||||
|     } | ||||
| @@ -581,7 +581,7 @@ void MR24HPC1Component::r24_frame_parse_human_information_(uint8_t *data) { | ||||
|              ((data[FRAME_COMMAND_WORD_INDEX] == 0x0A) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8A))) { | ||||
|     // none:0x00  1s:0x01 30s:0x02 1min:0x03 2min:0x04 5min:0x05 10min:0x06 30min:0x07 1hour:0x08 | ||||
|     if (data[FRAME_DATA_INDEX] < 9) { | ||||
|       this->unman_time_select_->publish_state(S_UNMANNED_TIME_STR[data[FRAME_DATA_INDEX]]); | ||||
|       this->unman_time_select_->publish_state(data[FRAME_DATA_INDEX]); | ||||
|     } | ||||
|   } else if ((this->keep_away_text_sensor_ != nullptr) && | ||||
|              ((data[FRAME_COMMAND_WORD_INDEX] == 0x0B) || (data[FRAME_COMMAND_WORD_INDEX] == 0x8B))) { | ||||
|   | ||||
| @@ -292,7 +292,7 @@ void MR60FDA2Component::process_frame_() { | ||||
|  | ||||
|         install_height_float = bit_cast<float>(current_install_height_int); | ||||
|         uint32_t select_index = find_nearest_index(install_height_float, INSTALL_HEIGHT, 7); | ||||
|         this->install_height_select_->publish_state(this->install_height_select_->at(select_index).value()); | ||||
|         this->install_height_select_->publish_state(select_index); | ||||
|       } | ||||
|  | ||||
|       if (this->height_threshold_select_ != nullptr) { | ||||
| @@ -301,7 +301,7 @@ void MR60FDA2Component::process_frame_() { | ||||
|  | ||||
|         height_threshold_float = bit_cast<float>(current_height_threshold_int); | ||||
|         size_t select_index = find_nearest_index(height_threshold_float, HEIGHT_THRESHOLD, 7); | ||||
|         this->height_threshold_select_->publish_state(this->height_threshold_select_->at(select_index).value()); | ||||
|         this->height_threshold_select_->publish_state(select_index); | ||||
|       } | ||||
|  | ||||
|       if (this->sensitivity_select_ != nullptr) { | ||||
| @@ -309,7 +309,7 @@ void MR60FDA2Component::process_frame_() { | ||||
|             encode_uint32(current_data_buf_[11], current_data_buf_[10], current_data_buf_[9], current_data_buf_[8]); | ||||
|  | ||||
|         uint32_t select_index = find_nearest_index(current_sensitivity, SENSITIVITY, 3); | ||||
|         this->sensitivity_select_->publish_state(this->sensitivity_select_->at(select_index).value()); | ||||
|         this->sensitivity_select_->publish_state(select_index); | ||||
|       } | ||||
|  | ||||
|       ESP_LOGD(TAG, "Mounting height: %.2f, Height threshold: %.2f, Sensitivity: %" PRIu32, install_height_float, | ||||
|   | ||||
| @@ -7,24 +7,39 @@ namespace select { | ||||
|  | ||||
| static const char *const TAG = "select"; | ||||
|  | ||||
| void Select::publish_state(const std::string &state) { | ||||
| void Select::publish_state(const std::string &state) { this->publish_state(state.c_str()); } | ||||
|  | ||||
| void Select::publish_state(const char *state) { | ||||
|   auto index = this->index_of(state); | ||||
|   const auto *name = this->get_name().c_str(); | ||||
|   if (index.has_value()) { | ||||
|     this->set_has_state(true); | ||||
|     this->state = state; | ||||
|     ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value()); | ||||
|     this->state_callback_.call(state, index.value()); | ||||
|     this->publish_state(index.value()); | ||||
|   } else { | ||||
|     ESP_LOGE(TAG, "'%s': invalid state for publish_state(): %s", name, state.c_str()); | ||||
|     ESP_LOGE(TAG, "'%s': Invalid option %s", this->get_name().c_str(), state); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void Select::publish_state(size_t index) { | ||||
|   if (!this->has_index(index)) { | ||||
|     ESP_LOGE(TAG, "'%s': Invalid index %zu", this->get_name().c_str(), index); | ||||
|     return; | ||||
|   } | ||||
|   const char *option = this->option_at(index); | ||||
|   this->set_has_state(true); | ||||
|   this->active_index_ = index; | ||||
|   ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", this->get_name().c_str(), option, index); | ||||
|   // Callback signature requires std::string, create temporary for compatibility | ||||
|   this->state_callback_.call(std::string(option), index); | ||||
| } | ||||
|  | ||||
| const char *Select::current_option() const { return this->option_at(this->active_index_); } | ||||
|  | ||||
| void Select::add_on_state_callback(std::function<void(std::string, size_t)> &&callback) { | ||||
|   this->state_callback_.add(std::move(callback)); | ||||
| } | ||||
|  | ||||
| bool Select::has_option(const std::string &option) const { return this->index_of(option).has_value(); } | ||||
| bool Select::has_option(const std::string &option) const { return this->index_of(option.c_str()).has_value(); } | ||||
|  | ||||
| bool Select::has_option(const char *option) const { return this->index_of(option).has_value(); } | ||||
|  | ||||
| bool Select::has_index(size_t index) const { return index < this->size(); } | ||||
|  | ||||
| @@ -33,10 +48,12 @@ size_t Select::size() const { | ||||
|   return options.size(); | ||||
| } | ||||
|  | ||||
| optional<size_t> Select::index_of(const std::string &option) const { | ||||
| optional<size_t> Select::index_of(const std::string &option) const { return this->index_of(option.c_str()); } | ||||
|  | ||||
| optional<size_t> Select::index_of(const char *option) const { | ||||
|   const auto &options = traits.get_options(); | ||||
|   for (size_t i = 0; i < options.size(); i++) { | ||||
|     if (strcmp(options[i], option.c_str()) == 0) { | ||||
|     if (strcmp(options[i], option) == 0) { | ||||
|       return i; | ||||
|     } | ||||
|   } | ||||
| @@ -45,19 +62,17 @@ optional<size_t> Select::index_of(const std::string &option) const { | ||||
|  | ||||
| optional<size_t> Select::active_index() const { | ||||
|   if (this->has_state()) { | ||||
|     return this->index_of(this->state); | ||||
|   } else { | ||||
|     return {}; | ||||
|     return this->active_index_; | ||||
|   } | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| optional<std::string> Select::at(size_t index) const { | ||||
|   if (this->has_index(index)) { | ||||
|     const auto &options = traits.get_options(); | ||||
|     return std::string(options.at(index)); | ||||
|   } else { | ||||
|     return {}; | ||||
|   } | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| const char *Select::option_at(size_t index) const { return traits.get_options().at(index); } | ||||
|   | ||||
| @@ -30,16 +30,21 @@ namespace select { | ||||
|  */ | ||||
| class Select : public EntityBase { | ||||
|  public: | ||||
|   std::string state; | ||||
|   SelectTraits traits; | ||||
|  | ||||
|   void publish_state(const std::string &state); | ||||
|   void publish_state(const char *state); | ||||
|   void publish_state(size_t index); | ||||
|  | ||||
|   /// Return the currently selected option (as const char* from flash). | ||||
|   const char *current_option() const; | ||||
|  | ||||
|   /// Instantiate a SelectCall object to modify this select component's state. | ||||
|   SelectCall make_call() { return SelectCall(this); } | ||||
|  | ||||
|   /// Return whether this select component contains the provided option. | ||||
|   bool has_option(const std::string &option) const; | ||||
|   bool has_option(const char *option) const; | ||||
|  | ||||
|   /// Return whether this select component contains the provided index offset. | ||||
|   bool has_index(size_t index) const; | ||||
| @@ -49,6 +54,7 @@ class Select : public EntityBase { | ||||
|  | ||||
|   /// Find the (optional) index offset of the provided option value. | ||||
|   optional<size_t> index_of(const std::string &option) const; | ||||
|   optional<size_t> index_of(const char *option) const; | ||||
|  | ||||
|   /// Return the (optional) index offset of the currently active option. | ||||
|   optional<size_t> active_index() const; | ||||
| @@ -61,12 +67,30 @@ class Select : public EntityBase { | ||||
|  | ||||
|   void add_on_state_callback(std::function<void(std::string, size_t)> &&callback); | ||||
|  | ||||
|   /** Set the value of the select by index, this is an optional virtual method. | ||||
|    * | ||||
|    * This method is called by the SelectCall when the index is already known. | ||||
|    * Default implementation converts to string and calls control(). | ||||
|    * Override this to work directly with indices and avoid string conversions. | ||||
|    * | ||||
|    * @param index The index as validated by the SelectCall. | ||||
|    */ | ||||
|   virtual void control(size_t index) { this->control(this->option_at(index)); } | ||||
|  | ||||
|  protected: | ||||
|   friend class SelectCall; | ||||
|  | ||||
|   size_t active_index_{0}; | ||||
|  | ||||
|   /** Set the value of the select, this is a virtual method that each select integration must implement. | ||||
|    * | ||||
|    * This method is called by the SelectCall. | ||||
|    * This method is called by control(size_t) when not overridden, or directly by external code. | ||||
|    * All existing integrations implement this method. New integrations can optionally override | ||||
|    * control(size_t) instead to work with indices directly and avoid string conversions. | ||||
|    * | ||||
|    * Delegation chain: | ||||
|    * - SelectCall::perform() → control(size_t) → [if not overridden] → control(string) | ||||
|    * - External code → control(string) → publish_state(string) → publish_state(size_t) | ||||
|    * | ||||
|    * @param value The value as validated by the SelectCall. | ||||
|    */ | ||||
|   | ||||
| @@ -11,6 +11,8 @@ SelectCall &SelectCall::set_option(const std::string &option) { | ||||
|   return with_operation(SELECT_OP_SET).with_option(option); | ||||
| } | ||||
|  | ||||
| SelectCall &SelectCall::set_option(const char *option) { return with_operation(SELECT_OP_SET).with_option(option); } | ||||
|  | ||||
| SelectCall &SelectCall::set_index(size_t index) { return with_operation(SELECT_OP_SET_INDEX).with_index(index); } | ||||
|  | ||||
| SelectCall &SelectCall::select_next(bool cycle) { return with_operation(SELECT_OP_NEXT).with_cycle(cycle); } | ||||
| @@ -31,8 +33,11 @@ SelectCall &SelectCall::with_cycle(bool cycle) { | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| SelectCall &SelectCall::with_option(const std::string &option) { | ||||
|   this->option_ = option; | ||||
| SelectCall &SelectCall::with_option(const std::string &option) { return this->with_option(option.c_str()); } | ||||
|  | ||||
| SelectCall &SelectCall::with_option(const char *option) { | ||||
|   // Find the option index - this validates the option exists | ||||
|   this->index_ = this->parent_->index_of(option); | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| @@ -56,64 +61,52 @@ void SelectCall::perform() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   std::string target_value; | ||||
|   size_t target_index; | ||||
|  | ||||
|   if (this->operation_ == SELECT_OP_SET) { | ||||
|     ESP_LOGD(TAG, "'%s' - Setting", name); | ||||
|     if (!this->option_.has_value()) { | ||||
|       ESP_LOGW(TAG, "'%s' - No option value set for SelectCall", name); | ||||
|       return; | ||||
|   if (this->operation_ == SELECT_OP_SET || this->operation_ == SELECT_OP_SET_INDEX) { | ||||
|     if (this->operation_ == SELECT_OP_SET) { | ||||
|       ESP_LOGD(TAG, "'%s' - Setting", name); | ||||
|     } | ||||
|     target_value = this->option_.value(); | ||||
|   } else if (this->operation_ == SELECT_OP_SET_INDEX) { | ||||
|     if (!this->index_.has_value()) { | ||||
|       ESP_LOGW(TAG, "'%s' - No index value set for SelectCall", name); | ||||
|       ESP_LOGW(TAG, "'%s' - No option value set for SelectCall", name); | ||||
|       return; | ||||
|     } | ||||
|     if (this->index_.value() >= options.size()) { | ||||
|       ESP_LOGW(TAG, "'%s' - Index value %zu out of bounds", name, this->index_.value()); | ||||
|       return; | ||||
|     } | ||||
|     target_value = options[this->index_.value()]; | ||||
|     target_index = this->index_.value(); | ||||
|   } else if (this->operation_ == SELECT_OP_FIRST) { | ||||
|     target_value = options.front(); | ||||
|     target_index = 0; | ||||
|   } else if (this->operation_ == SELECT_OP_LAST) { | ||||
|     target_value = options.back(); | ||||
|     target_index = options.size() - 1; | ||||
|   } else if (this->operation_ == SELECT_OP_NEXT || this->operation_ == SELECT_OP_PREVIOUS) { | ||||
|     auto cycle = this->cycle_; | ||||
|     ESP_LOGD(TAG, "'%s' - Selecting %s, with%s cycling", name, this->operation_ == SELECT_OP_NEXT ? "next" : "previous", | ||||
|              cycle ? "" : "out"); | ||||
|     if (!parent->has_state()) { | ||||
|       target_value = this->operation_ == SELECT_OP_NEXT ? options.front() : options.back(); | ||||
|       target_index = this->operation_ == SELECT_OP_NEXT ? 0 : options.size() - 1; | ||||
|     } else { | ||||
|       auto index = parent->index_of(parent->state); | ||||
|       if (index.has_value()) { | ||||
|         auto size = options.size(); | ||||
|         if (cycle) { | ||||
|           auto use_index = (size + index.value() + (this->operation_ == SELECT_OP_NEXT ? +1 : -1)) % size; | ||||
|           target_value = options[use_index]; | ||||
|         } else { | ||||
|           if (this->operation_ == SELECT_OP_PREVIOUS && index.value() > 0) { | ||||
|             target_value = options[index.value() - 1]; | ||||
|           } else if (this->operation_ == SELECT_OP_NEXT && index.value() < options.size() - 1) { | ||||
|             target_value = options[index.value() + 1]; | ||||
|           } else { | ||||
|             return; | ||||
|           } | ||||
|         } | ||||
|       // Use cached active_index_ instead of index_of() lookup | ||||
|       auto index = parent->active_index_; | ||||
|       auto size = options.size(); | ||||
|       if (cycle) { | ||||
|         target_index = (size + index + (this->operation_ == SELECT_OP_NEXT ? +1 : -1)) % size; | ||||
|       } else { | ||||
|         target_value = this->operation_ == SELECT_OP_NEXT ? options.front() : options.back(); | ||||
|         if (this->operation_ == SELECT_OP_PREVIOUS && index > 0) { | ||||
|           target_index = index - 1; | ||||
|         } else if (this->operation_ == SELECT_OP_NEXT && index < options.size() - 1) { | ||||
|           target_index = index + 1; | ||||
|         } else { | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!parent->has_option(target_value)) { | ||||
|     ESP_LOGW(TAG, "'%s' - Option %s is not a valid option", name, target_value.c_str()); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, target_value.c_str()); | ||||
|   parent->control(target_value); | ||||
|   // All operations use indices, call control() by index to avoid string conversion | ||||
|   ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, options[target_index]); | ||||
|   parent->control(target_index); | ||||
| } | ||||
|  | ||||
| }  // namespace select | ||||
|   | ||||
| @@ -23,6 +23,7 @@ class SelectCall { | ||||
|   void perform(); | ||||
|  | ||||
|   SelectCall &set_option(const std::string &option); | ||||
|   SelectCall &set_option(const char *option); | ||||
|   SelectCall &set_index(size_t index); | ||||
|  | ||||
|   SelectCall &select_next(bool cycle); | ||||
| @@ -33,11 +34,11 @@ class SelectCall { | ||||
|   SelectCall &with_operation(SelectOperation operation); | ||||
|   SelectCall &with_cycle(bool cycle); | ||||
|   SelectCall &with_option(const std::string &option); | ||||
|   SelectCall &with_option(const char *option); | ||||
|   SelectCall &with_index(size_t index); | ||||
|  | ||||
|  protected: | ||||
|   Select *const parent_; | ||||
|   optional<std::string> option_; | ||||
|   optional<size_t> index_; | ||||
|   SelectOperation operation_{SELECT_OP_NONE}; | ||||
|   bool cycle_; | ||||
|   | ||||
| @@ -24,7 +24,7 @@ void TemplateSelect::setup() { | ||||
|     ESP_LOGD(TAG, "State from initial: %s", this->option_at(index)); | ||||
|   } | ||||
|  | ||||
|   this->publish_state(this->at(index).value()); | ||||
|   this->publish_state(index); | ||||
| } | ||||
|  | ||||
| void TemplateSelect::update() { | ||||
| @@ -41,16 +41,14 @@ void TemplateSelect::update() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateSelect::control(const std::string &value) { | ||||
|   this->set_trigger_->trigger(value); | ||||
| void TemplateSelect::control(size_t index) { | ||||
|   this->set_trigger_->trigger(std::string(this->option_at(index))); | ||||
|  | ||||
|   if (this->optimistic_) | ||||
|     this->publish_state(value); | ||||
|     this->publish_state(index); | ||||
|  | ||||
|   if (this->restore_value_) { | ||||
|     auto index = this->index_of(value); | ||||
|     this->pref_.save(&index.value()); | ||||
|   } | ||||
|   if (this->restore_value_) | ||||
|     this->pref_.save(&index); | ||||
| } | ||||
|  | ||||
| void TemplateSelect::dump_config() { | ||||
|   | ||||
| @@ -24,7 +24,7 @@ class TemplateSelect : public select::Select, public PollingComponent { | ||||
|   void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
|   void control(size_t index) override; | ||||
|   bool optimistic_ = false; | ||||
|   size_t initial_option_index_{0}; | ||||
|   bool restore_value_ = false; | ||||
|   | ||||
| @@ -17,8 +17,7 @@ void TuyaSelect::setup() { | ||||
|       return; | ||||
|     } | ||||
|     size_t mapping_idx = std::distance(mappings.cbegin(), it); | ||||
|     auto value = this->at(mapping_idx); | ||||
|     this->publish_state(value.value()); | ||||
|     this->publish_state(mapping_idx); | ||||
|   }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1185,7 +1185,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM | ||||
|  | ||||
|     if (request->method() == HTTP_GET && match.method_empty()) { | ||||
|       auto detail = get_request_detail(request); | ||||
|       std::string data = this->select_json(obj, obj->state, detail); | ||||
|       std::string data = this->select_json(obj, obj->has_state() ? obj->current_option() : "", detail); | ||||
|       request->send(200, "application/json", data.c_str()); | ||||
|       return; | ||||
|     } | ||||
| @@ -1205,12 +1205,14 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM | ||||
|   request->send(404); | ||||
| } | ||||
| std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) { | ||||
|   return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_STATE); | ||||
|   auto *obj = (select::Select *) (source); | ||||
|   return web_server->select_json(obj, obj->has_state() ? obj->current_option() : "", DETAIL_STATE); | ||||
| } | ||||
| std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) { | ||||
|   return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL); | ||||
|   auto *obj = (select::Select *) (source); | ||||
|   return web_server->select_json(obj, obj->has_state() ? obj->current_option() : "", DETAIL_ALL); | ||||
| } | ||||
| std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) { | ||||
| std::string WebServer::select_json(select::Select *obj, const char *value, JsonDetail start_config) { | ||||
|   json::JsonBuilder builder; | ||||
|   JsonObject root = builder.root(); | ||||
|  | ||||
|   | ||||
| @@ -403,7 +403,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { | ||||
|   static std::string select_state_json_generator(WebServer *web_server, void *source); | ||||
|   static std::string select_all_json_generator(WebServer *web_server, void *source); | ||||
|   /// Dump the select state with its value as a JSON string. | ||||
|   std::string select_json(select::Select *obj, const std::string &value, JsonDetail start_config); | ||||
|   std::string select_json(select::Select *obj, const char *value, JsonDetail start_config); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_CLIMATE | ||||
|   | ||||
| @@ -744,7 +744,7 @@ def test_choose_upload_log_host_ota_local_all_options() -> None: | ||||
|         check_default=None, | ||||
|         purpose=Purpose.UPLOADING, | ||||
|     ) | ||||
|     assert result == ["MQTTIP", "test.local"] | ||||
|     assert result == ["MQTTIP"] | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures("mock_serial_ports") | ||||
| @@ -794,7 +794,7 @@ def test_choose_upload_log_host_ota_local_all_options_logging() -> None: | ||||
|         check_default=None, | ||||
|         purpose=Purpose.LOGGING, | ||||
|     ) | ||||
|     assert result == ["MQTTIP", "MQTT", "test.local"] | ||||
|     assert result == ["MQTTIP", "MQTT"] | ||||
|  | ||||
|  | ||||
| @pytest.mark.usefixtures("mock_no_mqtt_logging") | ||||
| @@ -1564,7 +1564,7 @@ def test_has_resolvable_address() -> None: | ||||
|     setup_core( | ||||
|         config={CONF_MDNS: {CONF_DISABLED: True}}, address="esphome-device.local" | ||||
|     ) | ||||
|     assert has_resolvable_address() is True | ||||
|     assert has_resolvable_address() is False | ||||
|  | ||||
|     # Test with mDNS disabled and regular DNS hostname (resolvable) | ||||
|     setup_core(config={CONF_MDNS: {CONF_DISABLED: True}}, address="device.example.com") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user