diff --git a/esphome/components/aqi/aqi_calculator.h b/esphome/components/aqi/aqi_calculator.h index d624af0432..bb8e402280 100644 --- a/esphome/components/aqi/aqi_calculator.h +++ b/esphome/components/aqi/aqi_calculator.h @@ -14,11 +14,7 @@ class AQICalculator : public AbstractAQICalculator { uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override { float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID); float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID); - - float aqi = std::max(pm2_5_index, pm10_0_index); - if (aqi < 0.0f) { - aqi = 0.0f; - } + float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f}); return static_cast(std::lround(aqi)); } diff --git a/esphome/components/aqi/caqi_calculator.h b/esphome/components/aqi/caqi_calculator.h index fe2efe7059..3f6da45aa9 100644 --- a/esphome/components/aqi/caqi_calculator.h +++ b/esphome/components/aqi/caqi_calculator.h @@ -12,11 +12,7 @@ class CAQICalculator : public AbstractAQICalculator { uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override { float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID); float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID); - - float aqi = std::max(pm2_5_index, pm10_0_index); - if (aqi < 0.0f) { - aqi = 0.0f; - } + float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f}); return static_cast(std::lround(aqi)); } diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 1e9cb42f38..87b8add2a3 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -85,7 +85,7 @@ void HonClimate::set_horizontal_airflow(hon_protocol::HorizontalSwingMode direct this->force_send_control_ = true; } -std::string HonClimate::get_cleaning_status_text() const { +const char *HonClimate::get_cleaning_status_text() const { switch (this->cleaning_status_) { case CleaningState::SELF_CLEAN: return "Self clean"; @@ -134,29 +134,22 @@ haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haie } // All OK hon_protocol::DeviceVersionAnswer *answr = (hon_protocol::DeviceVersionAnswer *) data; - char tmp[9]; - tmp[8] = 0; - strncpy(tmp, answr->protocol_version, 8); - this->hvac_hardware_info_ = HardwareInfo(); - this->hvac_hardware_info_.value().protocol_version_ = std::string(tmp); - strncpy(tmp, answr->software_version, 8); - this->hvac_hardware_info_.value().software_version_ = std::string(tmp); - strncpy(tmp, answr->hardware_version, 8); - this->hvac_hardware_info_.value().hardware_version_ = std::string(tmp); - strncpy(tmp, answr->device_name, 8); - this->hvac_hardware_info_.value().device_name_ = std::string(tmp); + HardwareInfo info{}; // zero-init guarantees null-termination + strncpy(info.protocol_version_, answr->protocol_version, HARDWARE_INFO_STR_SIZE - 1); + strncpy(info.software_version_, answr->software_version, HARDWARE_INFO_STR_SIZE - 1); + strncpy(info.hardware_version_, answr->hardware_version, HARDWARE_INFO_STR_SIZE - 1); + strncpy(info.device_name_, answr->device_name, HARDWARE_INFO_STR_SIZE - 1); + info.functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support + info.functions_[1] = (answr->functions[1] & 0x02) != 0; // controller-device mode support + info.functions_[2] = (answr->functions[1] & 0x04) != 0; // crc support + info.functions_[3] = (answr->functions[1] & 0x08) != 0; // multiple AC support + info.functions_[4] = (answr->functions[1] & 0x20) != 0; // roles support + this->use_crc_ = info.functions_[2]; #ifdef USE_TEXT_SENSOR - this->update_sub_text_sensor_(SubTextSensorType::APPLIANCE_NAME, this->hvac_hardware_info_.value().device_name_); - this->update_sub_text_sensor_(SubTextSensorType::PROTOCOL_VERSION, - this->hvac_hardware_info_.value().protocol_version_); + this->update_sub_text_sensor_(SubTextSensorType::APPLIANCE_NAME, info.device_name_); + this->update_sub_text_sensor_(SubTextSensorType::PROTOCOL_VERSION, info.protocol_version_); #endif - this->hvac_hardware_info_.value().functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support - this->hvac_hardware_info_.value().functions_[1] = - (answr->functions[1] & 0x02) != 0; // controller-device mode support - this->hvac_hardware_info_.value().functions_[2] = (answr->functions[1] & 0x04) != 0; // crc support - this->hvac_hardware_info_.value().functions_[3] = (answr->functions[1] & 0x08) != 0; // multiple AC support - this->hvac_hardware_info_.value().functions_[4] = (answr->functions[1] & 0x20) != 0; // roles support - this->use_crc_ = this->hvac_hardware_info_.value().functions_[2]; + this->hvac_hardware_info_ = info; this->set_phase(ProtocolPhases::SENDING_INIT_2); return result; } else { @@ -347,10 +340,9 @@ void HonClimate::dump_config() { " Device software version: %s\n" " Device hardware version: %s\n" " Device name: %s", - this->hvac_hardware_info_.value().protocol_version_.c_str(), - this->hvac_hardware_info_.value().software_version_.c_str(), - this->hvac_hardware_info_.value().hardware_version_.c_str(), - this->hvac_hardware_info_.value().device_name_.c_str()); + this->hvac_hardware_info_.value().protocol_version_, + this->hvac_hardware_info_.value().software_version_, + this->hvac_hardware_info_.value().hardware_version_, this->hvac_hardware_info_.value().device_name_); ESP_LOGCONFIG(TAG, " Device features:%s%s%s%s%s", (this->hvac_hardware_info_.value().functions_[0] ? " interactive" : ""), (this->hvac_hardware_info_.value().functions_[1] ? " controller-device" : ""), @@ -460,7 +452,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { if (this->action_request_.has_value()) { if (this->action_request_.value().message.has_value()) { this->send_message_(this->action_request_.value().message.value(), this->use_crc_); - this->action_request_.value().message.reset(); + this->action_request_.value().message.reset(); // NOLINT(bugprone-unchecked-optional-access) } else { // Message already sent, reseting request and return to idle this->action_request_.reset(); @@ -796,7 +788,7 @@ void HonClimate::set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSe } } -void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::string &value) { +void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const char *value) { size_t index = (size_t) type; if (this->sub_text_sensors_[index] != nullptr) this->sub_text_sensors_[index]->publish_state(value); diff --git a/esphome/components/haier/hon_climate.h b/esphome/components/haier/hon_climate.h index 7a87f27b66..a0bcdfb548 100644 --- a/esphome/components/haier/hon_climate.h +++ b/esphome/components/haier/hon_climate.h @@ -90,7 +90,7 @@ class HonClimate : public HaierClimateBase { void set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSensor *sens); protected: - void update_sub_text_sensor_(SubTextSensorType type, const std::string &value); + void update_sub_text_sensor_(SubTextSensorType type, const char *value); text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr}; #endif #ifdef USE_SWITCH @@ -116,7 +116,7 @@ class HonClimate : public HaierClimateBase { void set_vertical_airflow(hon_protocol::VerticalSwingMode direction); esphome::optional get_horizontal_airflow() const; void set_horizontal_airflow(hon_protocol::HorizontalSwingMode direction); - std::string get_cleaning_status_text() const; + const char *get_cleaning_status_text() const; CleaningState get_cleaning_status() const; void start_self_cleaning(); void start_steri_cleaning(); @@ -166,11 +166,12 @@ class HonClimate : public HaierClimateBase { void fill_control_messages_queue_(); void clear_control_messages_queue_(); + static constexpr size_t HARDWARE_INFO_STR_SIZE = 9; struct HardwareInfo { - std::string protocol_version_; - std::string software_version_; - std::string hardware_version_; - std::string device_name_; + char protocol_version_[HARDWARE_INFO_STR_SIZE]; + char software_version_[HARDWARE_INFO_STR_SIZE]; + char hardware_version_[HARDWARE_INFO_STR_SIZE]; + char device_name_[HARDWARE_INFO_STR_SIZE]; bool functions_[5]; }; diff --git a/esphome/components/haier/smartair2_climate.cpp b/esphome/components/haier/smartair2_climate.cpp index 200cac2557..752f4d4f1c 100644 --- a/esphome/components/haier/smartair2_climate.cpp +++ b/esphome/components/haier/smartair2_climate.cpp @@ -191,7 +191,7 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) if (this->action_request_.has_value()) { if (this->action_request_.value().message.has_value()) { this->send_message_(this->action_request_.value().message.value(), this->use_crc_); - this->action_request_.value().message.reset(); + this->action_request_.value().message.reset(); // NOLINT(bugprone-unchecked-optional-access) } else { // Message already sent, reseting request and return to idle this->action_request_.reset(); diff --git a/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp b/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp index 9f2557243c..9bebd4cd56 100644 --- a/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +++ b/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp @@ -139,12 +139,12 @@ void KamstrupKMPComponent::clear_uart_rx_buffer_() { void KamstrupKMPComponent::read_command_(uint16_t command) { uint8_t buffer[20] = {0}; - int buffer_len = 0; + size_t buffer_len = 0; int data; int timeout = 250; // ms // Read the data from the UART - while (timeout > 0 && buffer_len < static_cast(sizeof(buffer))) { + while (timeout > 0 && buffer_len < sizeof(buffer)) { if (this->available()) { data = this->read(); if (data > -1) { @@ -183,7 +183,7 @@ void KamstrupKMPComponent::read_command_(uint16_t command) { // Decode uint8_t msg[20] = {0}; int msg_len = 0; - for (int i = 1; i < buffer_len - 1; i++) { + for (size_t i = 1; i < buffer_len - 1; i++) { if (buffer[i] == 0x1B) { msg[msg_len++] = buffer[i + 1] ^ 0xFF; i++; diff --git a/esphome/components/mixer/speaker/mixer_speaker.cpp b/esphome/components/mixer/speaker/mixer_speaker.cpp index 741239a2dd..0d16bce330 100644 --- a/esphome/components/mixer/speaker/mixer_speaker.cpp +++ b/esphome/components/mixer/speaker/mixer_speaker.cpp @@ -588,6 +588,7 @@ void MixerSpeaker::mix_audio_samples(const int16_t *primary_buffer, audio::Audio } } +// NOLINTBEGIN(bugprone-unchecked-optional-access) -- audio_stream_info_ always set before this task is created void MixerSpeaker::audio_mixer_task(void *params) { MixerSpeaker *this_mixer = static_cast(params); @@ -764,6 +765,7 @@ void MixerSpeaker::audio_mixer_task(void *params) { vTaskSuspend(nullptr); // Suspend this task indefinitely until the loop method deletes it } +// NOLINTEND(bugprone-unchecked-optional-access) } // namespace esphome::mixer_speaker diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index 53114cc50f..a23b4c7cc3 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -275,7 +275,7 @@ static Ras2819tSecondPacketCodes get_ras_2819t_second_packet_codes(climate::Clim */ static uint8_t get_ras_2819t_temp_code(float temperature) { int temp_index = static_cast(temperature) - 18; - if (temp_index < 0 || temp_index >= static_cast(sizeof(RAS_2819T_TEMP_CODES))) { + if (temp_index < 0 || static_cast(temp_index) >= sizeof(RAS_2819T_TEMP_CODES)) { ESP_LOGW(TAG, "Temperature %.1f°C out of range [18-30°C], defaulting to 24°C", temperature); return 0x40; // Default to 24°C } diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 24a76e95b6..6e36aa14a2 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -60,8 +60,9 @@ struct SchedulerNameLog { return buffer; } else { // SELF_POINTER // static_name carries the void* key for SELF_POINTER (pointer-width union slot). - // Cast to const void* — %p requires a void* argument. - ESPHOME_snprintf_P(buffer, sizeof(buffer), ESPHOME_PSTR("self:%p"), static_cast(static_name)); + // %p is specified as void* (not const void*), so strip const for the varargs call. + ESPHOME_snprintf_P(buffer, sizeof(buffer), ESPHOME_PSTR("self:%p"), + const_cast(static_cast(static_name))); return buffer; } } diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index a97f45b15f..7a6be6bea9 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -146,7 +146,8 @@ class Scheduler { } // Name storage type discriminator for SchedulerItem - // Used to distinguish between static strings, hashed strings, numeric IDs, and internal numeric IDs + // Used to distinguish between static strings, hashed strings, numeric IDs, internal numeric IDs, + // and self-keyed pointers (caller-supplied `void *`, typically `this`). enum class NameType : uint8_t { STATIC_STRING = 0, // const char* pointer to static/flash storage HASHED_STRING = 1, // uint32_t FNV-1a hash of a runtime string diff --git a/tests/integration/test_scheduler_self_keyed.py b/tests/integration/test_scheduler_self_keyed.py index 48c9d98ad6..e0825ea825 100644 --- a/tests/integration/test_scheduler_self_keyed.py +++ b/tests/integration/test_scheduler_self_keyed.py @@ -1,9 +1,10 @@ """Test the self-keyed scheduler API. -Verifies that `Scheduler::set_timeout(void *, ...)` / `set_interval(void *, ...)` and the -matching `cancel_*(void *)` overloads behave correctly: callbacks fire, distinct keys -don't collide, self-keyed and component-keyed namespaces are independent, and -re-registering the same key replaces the existing timer. +Verifies that `Scheduler::set_timeout(const void *, ...)` / +`set_interval(const void *, ...)` and the matching `cancel_*(const void *)` +overloads behave correctly: callbacks fire, distinct keys don't collide, +self-keyed and component-keyed namespaces are independent, and re-registering +the same key replaces the existing timer. """ import asyncio