diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 37990aeec5..b1dfe1bc9a 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -52,7 +52,7 @@ void DebugComponent::on_shutdown() { char buffer[REBOOT_MAX_LEN]{}; auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name())); if (component != nullptr) { - strncpy(buffer, component->get_component_source(), REBOOT_MAX_LEN - 1); + strncpy(buffer, LOG_STR_ARG(component->get_component_log_str()), REBOOT_MAX_LEN - 1); buffer[REBOOT_MAX_LEN - 1] = '\0'; } ESP_LOGD(TAG, "Storing reboot source: %s", buffer); diff --git a/esphome/components/runtime_stats/runtime_stats.cpp b/esphome/components/runtime_stats/runtime_stats.cpp index 8f5d5daf01..2da517b1f4 100644 --- a/esphome/components/runtime_stats/runtime_stats.cpp +++ b/esphome/components/runtime_stats/runtime_stats.cpp @@ -17,16 +17,8 @@ void RuntimeStatsCollector::record_component_time(Component *component, uint32_t if (component == nullptr) return; - // Check if we have cached the name for this component - auto name_it = this->component_names_cache_.find(component); - if (name_it == this->component_names_cache_.end()) { - // First time seeing this component, cache its name - const char *source = component->get_component_source(); - this->component_names_cache_[component] = source; - this->component_stats_[source].record_time(duration_ms); - } else { - this->component_stats_[name_it->second].record_time(duration_ms); - } + // Record stats using component pointer as key + this->component_stats_[component].record_time(duration_ms); if (this->next_log_time_ == 0) { this->next_log_time_ = current_time + this->log_interval_; @@ -42,9 +34,10 @@ void RuntimeStatsCollector::log_stats_() { std::vector stats_to_display; for (const auto &it : this->component_stats_) { + Component *component = it.first; const ComponentRuntimeStats &stats = it.second; if (stats.get_period_count() > 0) { - ComponentStatPair pair = {it.first, &stats}; + ComponentStatPair pair = {component, &stats}; stats_to_display.push_back(pair); } } @@ -54,7 +47,7 @@ void RuntimeStatsCollector::log_stats_() { // Log top components by period runtime for (const auto &it : stats_to_display) { - const char *source = it.name; + const char *source = LOG_STR_ARG(it.component->get_component_log_str()); const ComponentRuntimeStats *stats = it.stats; ESP_LOGI(TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source, @@ -72,7 +65,7 @@ void RuntimeStatsCollector::log_stats_() { }); for (const auto &it : stats_to_display) { - const char *source = it.name; + const char *source = LOG_STR_ARG(it.component->get_component_log_str()); const ComponentRuntimeStats *stats = it.stats; ESP_LOGI(TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source, diff --git a/esphome/components/runtime_stats/runtime_stats.h b/esphome/components/runtime_stats/runtime_stats.h index e2f8bee563..56122364c2 100644 --- a/esphome/components/runtime_stats/runtime_stats.h +++ b/esphome/components/runtime_stats/runtime_stats.h @@ -79,7 +79,7 @@ class ComponentRuntimeStats { // For sorting components by run time struct ComponentStatPair { - const char *name; + Component *component; const ComponentRuntimeStats *stats; bool operator>(const ComponentStatPair &other) const { @@ -109,15 +109,9 @@ class RuntimeStatsCollector { } } - // Use const char* keys for efficiency - // Custom comparator for const char* keys in map - // Without this, std::map would compare pointer addresses instead of string contents, - // causing identical component names at different addresses to be treated as different keys - struct CStrCompare { - bool operator()(const char *a, const char *b) const { return std::strcmp(a, b) < 0; } - }; - std::map component_stats_; - std::map component_names_cache_; + // Map from component to its stats + // We use Component* as the key since each component is unique + std::map component_stats_; uint32_t log_interval_; uint32_t next_log_time_; }; diff --git a/esphome/components/sensirion_common/i2c_sensirion.h b/esphome/components/sensirion_common/i2c_sensirion.h index aba93d6cc3..e141591525 100644 --- a/esphome/components/sensirion_common/i2c_sensirion.h +++ b/esphome/components/sensirion_common/i2c_sensirion.h @@ -15,6 +15,8 @@ namespace sensirion_common { * Format: * | 16 Bit Command Code | 16 bit Data word 1 | CRC of DW 1 | 16 bit Data word 1 | CRC of DW 2 | .. */ +static const uint8_t CRC_POLYNOMIAL = 0x31; // default for Sensirion + class SensirionI2CDevice : public i2c::I2CDevice { public: enum CommandLen : uint8_t { ADDR_8_BIT = 1, ADDR_16_BIT = 2 }; diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 42baff6d23..0e3aeff812 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,9 +1,11 @@ #include "sgp30.h" -#include #include "esphome/core/application.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace sgp30 { @@ -39,9 +41,8 @@ void SGP30Component::setup() { this->mark_failed(); return; } - this->serial_number_ = (uint64_t(raw_serial_number[0]) << 24) | (uint64_t(raw_serial_number[1]) << 16) | - (uint64_t(raw_serial_number[2])); - ESP_LOGD(TAG, "Serial Number: %" PRIu64, this->serial_number_); + this->serial_number_ = encode_uint24(raw_serial_number[0], raw_serial_number[1], raw_serial_number[2]); + ESP_LOGD(TAG, "Serial number: %" PRIu64, this->serial_number_); // Featureset identification for future use uint16_t raw_featureset; @@ -61,11 +62,11 @@ void SGP30Component::setup() { this->mark_failed(); return; } - ESP_LOGD(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF)); + ESP_LOGV(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF)); // Sensor initialization if (!this->write_command(SGP30_CMD_IAQ_INIT)) { - ESP_LOGE(TAG, "Sensor sgp30_iaq_init failed."); + ESP_LOGE(TAG, "sgp30_iaq_init failed"); this->error_code_ = MEASUREMENT_INIT_FAILED; this->mark_failed(); return; @@ -123,7 +124,7 @@ void SGP30Component::read_iaq_baseline_() { uint16_t eco2baseline = (raw_data[0]); uint16_t tvocbaseline = (raw_data[1]); - ESP_LOGI(TAG, "Current eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2baseline, tvocbaseline); + ESP_LOGI(TAG, "Baselines: eCO2: 0x%04X, TVOC: 0x%04X", eco2baseline, tvocbaseline); if (eco2baseline != this->eco2_baseline_ || tvocbaseline != this->tvoc_baseline_) { this->eco2_baseline_ = eco2baseline; this->tvoc_baseline_ = tvocbaseline; @@ -142,7 +143,7 @@ void SGP30Component::read_iaq_baseline_() { this->baselines_storage_.eco2 = this->eco2_baseline_; this->baselines_storage_.tvoc = this->tvoc_baseline_; if (this->pref_.save(&this->baselines_storage_)) { - ESP_LOGI(TAG, "Store eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, + ESP_LOGI(TAG, "Store baselines: eCO2: 0x%04X, TVOC: 0x%04X", this->baselines_storage_.eco2, this->baselines_storage_.tvoc); } else { ESP_LOGW(TAG, "Could not store eCO2 and TVOC baselines"); @@ -164,7 +165,7 @@ void SGP30Component::send_env_data_() { if (this->humidity_sensor_ != nullptr) humidity = this->humidity_sensor_->state; if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { - ESP_LOGW(TAG, "Compensation not possible yet: bad humidity data."); + ESP_LOGW(TAG, "Compensation not possible yet: bad humidity data"); return; } else { ESP_LOGD(TAG, "External compensation data received: Humidity %0.2f%%", humidity); @@ -174,7 +175,7 @@ void SGP30Component::send_env_data_() { temperature = float(this->temperature_sensor_->state); } if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { - ESP_LOGW(TAG, "Compensation not possible yet: bad temperature value data."); + ESP_LOGW(TAG, "Compensation not possible yet: bad temperature value"); return; } else { ESP_LOGD(TAG, "External compensation data received: Temperature %0.2f°C", temperature); @@ -192,18 +193,17 @@ void SGP30Component::send_env_data_() { ((humidity * 0.061121f * std::exp((18.678f - temperature / 234.5f) * (temperature / (257.14f + temperature)))) / (273.15f + temperature)); } - uint8_t humidity_full = uint8_t(std::floor(absolute_humidity)); - uint8_t humidity_dec = uint8_t(std::floor((absolute_humidity - std::floor(absolute_humidity)) * 256)); - ESP_LOGD(TAG, "Calculated Absolute humidity: %0.3f g/m³ (0x%04X)", absolute_humidity, - uint16_t(uint16_t(humidity_full) << 8 | uint16_t(humidity_dec))); - uint8_t crc = sht_crc_(humidity_full, humidity_dec); - uint8_t data[4]; - data[0] = SGP30_CMD_SET_ABSOLUTE_HUMIDITY & 0xFF; - data[1] = humidity_full; - data[2] = humidity_dec; - data[3] = crc; + uint8_t data[4] = { + SGP30_CMD_SET_ABSOLUTE_HUMIDITY & 0xFF, + uint8_t(std::floor(absolute_humidity)), // humidity_full + uint8_t(std::floor((absolute_humidity - std::floor(absolute_humidity)) * 256)), // humidity_dec + 0, + }; + data[3] = crc8(&data[1], 2, 0xFF, sensirion_common::CRC_POLYNOMIAL, true); + ESP_LOGD(TAG, "Calculated absolute humidity: %0.3f g/m³ (0x%04X)", absolute_humidity, + encode_uint16(data[1], data[2])); if (!this->write_bytes(SGP30_CMD_SET_ABSOLUTE_HUMIDITY >> 8, data, 4)) { - ESP_LOGE(TAG, "Error sending compensation data."); + ESP_LOGE(TAG, "Error sending compensation data"); } } @@ -212,15 +212,14 @@ void SGP30Component::write_iaq_baseline_(uint16_t eco2_baseline, uint16_t tvoc_b data[0] = SGP30_CMD_SET_IAQ_BASELINE & 0xFF; data[1] = tvoc_baseline >> 8; data[2] = tvoc_baseline & 0xFF; - data[3] = sht_crc_(data[1], data[2]); + data[3] = crc8(&data[1], 2, 0xFF, sensirion_common::CRC_POLYNOMIAL, true); data[4] = eco2_baseline >> 8; data[5] = eco2_baseline & 0xFF; - data[6] = sht_crc_(data[4], data[5]); + data[6] = crc8(&data[4], 2, 0xFF, sensirion_common::CRC_POLYNOMIAL, true); if (!this->write_bytes(SGP30_CMD_SET_IAQ_BASELINE >> 8, data, 7)) { - ESP_LOGE(TAG, "Error applying eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2_baseline, tvoc_baseline); + ESP_LOGE(TAG, "Error applying baselines: eCO2: 0x%04X, TVOC: 0x%04X", eco2_baseline, tvoc_baseline); } else { - ESP_LOGI(TAG, "Initial baselines applied successfully! eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2_baseline, - tvoc_baseline); + ESP_LOGI(TAG, "Initial baselines applied: eCO2: 0x%04X, TVOC: 0x%04X", eco2_baseline, tvoc_baseline); } } @@ -236,10 +235,10 @@ void SGP30Component::dump_config() { ESP_LOGW(TAG, "Measurement Initialization failed"); break; case INVALID_ID: - ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this an SGP30?"); + ESP_LOGW(TAG, "Invalid ID"); break; case UNSUPPORTED_ID: - ESP_LOGW(TAG, "Sensor reported an unsupported ID (SGPC3)"); + ESP_LOGW(TAG, "Unsupported ID"); break; default: ESP_LOGW(TAG, "Unknown setup error"); @@ -249,12 +248,12 @@ void SGP30Component::dump_config() { ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); if (this->eco2_baseline_ != 0x0000 && this->tvoc_baseline_ != 0x0000) { ESP_LOGCONFIG(TAG, - " Baseline:\n" - " eCO2 Baseline: 0x%04X\n" - " TVOC Baseline: 0x%04X", + " Baselines:\n" + " eCO2: 0x%04X\n" + " TVOC: 0x%04X", this->eco2_baseline_, this->tvoc_baseline_); } else { - ESP_LOGCONFIG(TAG, " Baseline: No baseline configured"); + ESP_LOGCONFIG(TAG, " Baselines not configured"); } ESP_LOGCONFIG(TAG, " Warm up time: %" PRIu32 "s", this->required_warm_up_time_); } @@ -266,8 +265,8 @@ void SGP30Component::dump_config() { ESP_LOGCONFIG(TAG, "Store baseline: %s", YESNO(this->store_baseline_)); if (this->humidity_sensor_ != nullptr && this->temperature_sensor_ != nullptr) { ESP_LOGCONFIG(TAG, " Compensation:"); - LOG_SENSOR(" ", "Temperature Source:", this->temperature_sensor_); - LOG_SENSOR(" ", "Humidity Source:", this->humidity_sensor_); + LOG_SENSOR(" ", "Temperature source:", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity source:", this->humidity_sensor_); } else { ESP_LOGCONFIG(TAG, " Compensation: No source configured"); } @@ -289,7 +288,7 @@ void SGP30Component::update() { float eco2 = (raw_data[0]); float tvoc = (raw_data[1]); - ESP_LOGD(TAG, "Got eCO2=%.1fppm TVOC=%.1fppb", eco2, tvoc); + ESP_LOGV(TAG, "eCO2=%.1fppm TVOC=%.1fppb", eco2, tvoc); if (this->eco2_sensor_ != nullptr) this->eco2_sensor_->publish_state(eco2); if (this->tvoc_sensor_ != nullptr) diff --git a/esphome/components/sgp30/sgp30.h b/esphome/components/sgp30/sgp30.h index e6429a7bfa..4648a33e15 100644 --- a/esphome/components/sgp30/sgp30.h +++ b/esphome/components/sgp30/sgp30.h @@ -1,8 +1,8 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensirion_common/i2c_sensirion.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" #include "esphome/core/preferences.h" #include @@ -38,14 +38,16 @@ class SGP30Component : public PollingComponent, public sensirion_common::Sensiri void read_iaq_baseline_(); bool is_sensor_baseline_reliable_(); void write_iaq_baseline_(uint16_t eco2_baseline, uint16_t tvoc_baseline); + uint64_t serial_number_; - uint16_t featureset_; uint32_t required_warm_up_time_; uint32_t seconds_since_last_store_; - SGP30Baselines baselines_storage_; - ESPPreferenceObject pref_; + uint16_t featureset_; + uint16_t eco2_baseline_{0x0000}; + uint16_t tvoc_baseline_{0x0000}; + bool store_baseline_; - enum ErrorCode { + enum ErrorCode : uint8_t { COMMUNICATION_FAILED, MEASUREMENT_INIT_FAILED, INVALID_ID, @@ -53,14 +55,13 @@ class SGP30Component : public PollingComponent, public sensirion_common::Sensiri UNKNOWN } error_code_{UNKNOWN}; + ESPPreferenceObject pref_; + SGP30Baselines baselines_storage_; + sensor::Sensor *eco2_sensor_{nullptr}; sensor::Sensor *tvoc_sensor_{nullptr}; sensor::Sensor *eco2_sensor_baseline_{nullptr}; sensor::Sensor *tvoc_sensor_baseline_{nullptr}; - uint16_t eco2_baseline_{0x0000}; - uint16_t tvoc_baseline_{0x0000}; - bool store_baseline_; - /// Input sensor for humidity and temperature compensation. sensor::Sensor *humidity_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr}; diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 272acc78f2..b99bf416d6 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -43,20 +43,20 @@ void SPS30Component::setup() { this->serial_number_[i * 2] = static_cast(raw_serial_number[i] >> 8); this->serial_number_[i * 2 + 1] = uint16_t(uint16_t(raw_serial_number[i] & 0xFF)); } - ESP_LOGD(TAG, " Serial Number: '%s'", this->serial_number_); + ESP_LOGV(TAG, " Serial number: %s", this->serial_number_); bool result; if (this->fan_interval_.has_value()) { // override default value - result = write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS, this->fan_interval_.value()); + result = this->write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS, this->fan_interval_.value()); } else { - result = write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS); + result = this->write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS); } if (result) { delay(20); uint16_t secs[2]; if (this->read_data(secs, 2)) { - fan_interval_ = secs[0] << 16 | secs[1]; + this->fan_interval_ = secs[0] << 16 | secs[1]; } } @@ -67,7 +67,7 @@ void SPS30Component::setup() { } void SPS30Component::dump_config() { - ESP_LOGCONFIG(TAG, "sps30:"); + ESP_LOGCONFIG(TAG, "SPS30:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { switch (this->error_code_) { @@ -78,16 +78,16 @@ void SPS30Component::dump_config() { ESP_LOGW(TAG, "Measurement Initialization failed"); break; case SERIAL_NUMBER_REQUEST_FAILED: - ESP_LOGW(TAG, "Unable to request sensor serial number"); + ESP_LOGW(TAG, "Unable to request serial number"); break; case SERIAL_NUMBER_READ_FAILED: - ESP_LOGW(TAG, "Unable to read sensor serial number"); + ESP_LOGW(TAG, "Unable to read serial number"); break; case FIRMWARE_VERSION_REQUEST_FAILED: - ESP_LOGW(TAG, "Unable to request sensor firmware version"); + ESP_LOGW(TAG, "Unable to request firmware version"); break; case FIRMWARE_VERSION_READ_FAILED: - ESP_LOGW(TAG, "Unable to read sensor firmware version"); + ESP_LOGW(TAG, "Unable to read firmware version"); break; default: ESP_LOGW(TAG, "Unknown setup error"); @@ -96,9 +96,9 @@ void SPS30Component::dump_config() { } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, - " Serial Number: '%s'\n" + " Serial number: %s\n" " Firmware version v%0d.%0d", - this->serial_number_, (raw_firmware_version_ >> 8), uint16_t(raw_firmware_version_ & 0xFF)); + this->serial_number_, this->raw_firmware_version_ >> 8, this->raw_firmware_version_ & 0xFF); LOG_SENSOR(" ", "PM1.0 Weight Concentration", this->pm_1_0_sensor_); LOG_SENSOR(" ", "PM2.5 Weight Concentration", this->pm_2_5_sensor_); LOG_SENSOR(" ", "PM4 Weight Concentration", this->pm_4_0_sensor_); @@ -113,15 +113,15 @@ void SPS30Component::dump_config() { void SPS30Component::update() { /// Check if warning flag active (sensor reconnected?) if (this->status_has_warning()) { - ESP_LOGD(TAG, "Trying to reconnect"); + ESP_LOGD(TAG, "Reconnecting"); if (this->write_command(SPS30_CMD_SOFT_RESET)) { - ESP_LOGD(TAG, "Soft-reset successful. Waiting for reconnection in 500 ms"); + ESP_LOGD(TAG, "Soft-reset successful; waiting 500 ms"); this->set_timeout(500, [this]() { this->start_continuous_measurement_(); /// Sensor restarted and reading attempt made next cycle this->status_clear_warning(); this->skipped_data_read_cycles_ = 0; - ESP_LOGD(TAG, "Reconnect successful. Resuming continuous measurement"); + ESP_LOGD(TAG, "Reconnected; resuming continuous measurement"); }); } else { ESP_LOGD(TAG, "Soft-reset failed"); @@ -136,12 +136,12 @@ void SPS30Component::update() { uint16_t raw_read_status; if (!this->read_data(&raw_read_status, 1) || raw_read_status == 0x00) { - ESP_LOGD(TAG, "Not ready yet"); + ESP_LOGD(TAG, "Not ready"); this->skipped_data_read_cycles_++; /// The following logic is required to address the cases when a sensor is quickly replaced before it's marked /// as failed so that new sensor is eventually forced to be reinitialized for continuous measurement. if (this->skipped_data_read_cycles_ > MAX_SKIPPED_DATA_CYCLES_BEFORE_ERROR) { - ESP_LOGD(TAG, "Exceeded max allowed attempts; communication will be reinitialized"); + ESP_LOGD(TAG, "Exceeded max attempts; will reinitialize"); this->status_set_warning(); } return; @@ -211,11 +211,6 @@ void SPS30Component::update() { } bool SPS30Component::start_continuous_measurement_() { - uint8_t data[4]; - data[0] = SPS30_CMD_START_CONTINUOUS_MEASUREMENTS & 0xFF; - data[1] = 0x03; - data[2] = 0x00; - data[3] = sht_crc_(0x03, 0x00); if (!this->write_command(SPS30_CMD_START_CONTINUOUS_MEASUREMENTS, SPS30_CMD_START_CONTINUOUS_MEASUREMENTS_ARG)) { ESP_LOGE(TAG, "Error initiating measurements"); return false; @@ -224,9 +219,9 @@ bool SPS30Component::start_continuous_measurement_() { } bool SPS30Component::start_fan_cleaning() { - if (!write_command(SPS30_CMD_START_FAN_CLEANING)) { + if (!this->write_command(SPS30_CMD_START_FAN_CLEANING)) { this->status_set_warning(); - ESP_LOGE(TAG, "write error start fan (%d)", this->last_error_); + ESP_LOGE(TAG, "Start fan cleaning failed (%d)", this->last_error_); return false; } else { ESP_LOGD(TAG, "Fan auto clean started"); diff --git a/esphome/components/sps30/sps30.h b/esphome/components/sps30/sps30.h index 04189247e8..461a770ab6 100644 --- a/esphome/components/sps30/sps30.h +++ b/esphome/components/sps30/sps30.h @@ -30,12 +30,12 @@ class SPS30Component : public PollingComponent, public sensirion_common::Sensiri bool start_fan_cleaning(); protected: - char serial_number_[17] = {0}; /// Terminating NULL character uint16_t raw_firmware_version_; - bool start_continuous_measurement_(); + char serial_number_[17] = {0}; /// Terminating NULL character uint8_t skipped_data_read_cycles_ = 0; + bool start_continuous_measurement_(); - enum ErrorCode { + enum ErrorCode : uint8_t { COMMUNICATION_FAILED, FIRMWARE_VERSION_REQUEST_FAILED, FIRMWARE_VERSION_READ_FAILED, diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index dc745a2a46..b78f6fb903 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -80,7 +80,7 @@ void Application::register_component_(Component *comp) { for (auto *c : this->components_) { if (comp == c) { - ESP_LOGW(TAG, "Component %s already registered! (%p)", c->get_component_source(), c); + ESP_LOGW(TAG, "Component %s already registered! (%p)", LOG_STR_ARG(c->get_component_log_str()), c); return; } } @@ -340,8 +340,8 @@ void Application::teardown_components(uint32_t timeout_ms) { // Note: At this point, connections are either disconnected or in a bad state, // so this warning will only appear via serial rather than being transmitted to clients for (size_t i = 0; i < pending_count; ++i) { - ESP_LOGW(TAG, "%s did not complete teardown within %" PRIu32 " ms", pending_components[i]->get_component_source(), - timeout_ms); + ESP_LOGW(TAG, "%s did not complete teardown within %" PRIu32 " ms", + LOG_STR_ARG(pending_components[i]->get_component_log_str()), timeout_ms); } } } @@ -473,7 +473,7 @@ void Application::enable_pending_loops_() { // Clear the pending flag and enable the loop component->pending_enable_loop_ = false; - ESP_LOGVV(TAG, "%s loop enabled from ISR", component->get_component_source()); + ESP_LOGVV(TAG, "%s loop enabled from ISR", LOG_STR_ARG(component->get_component_log_str())); component->component_state_ &= ~COMPONENT_STATE_MASK; component->component_state_ |= COMPONENT_STATE_LOOP; diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 40cda17ca3..3f6beeb28e 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -142,7 +142,7 @@ void Component::call_dump_config() { } } } - ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(), + ESP_LOGE(TAG, " %s is marked FAILED: %s", LOG_STR_ARG(this->get_component_log_str()), error_msg ? error_msg : UNSPECIFIED_MESSAGE); } } @@ -154,14 +154,14 @@ void Component::call() { case COMPONENT_STATE_CONSTRUCTION: { // State Construction: Call setup and set state to setup this->set_component_state_(COMPONENT_STATE_SETUP); - ESP_LOGV(TAG, "Setup %s", this->get_component_source()); + ESP_LOGV(TAG, "Setup %s", LOG_STR_ARG(this->get_component_log_str())); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG uint32_t start_time = millis(); #endif this->call_setup(); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG uint32_t setup_time = millis() - start_time; - ESP_LOGCONFIG(TAG, "Setup %s took %ums", this->get_component_source(), (unsigned) setup_time); + ESP_LOGCONFIG(TAG, "Setup %s took %ums", LOG_STR_ARG(this->get_component_log_str()), (unsigned) setup_time); #endif break; } @@ -182,10 +182,8 @@ void Component::call() { break; } } -const char *Component::get_component_source() const { - if (this->component_source_ == nullptr) - return ""; - return this->component_source_; +const LogString *Component::get_component_log_str() const { + return this->component_source_ == nullptr ? LOG_STR("") : this->component_source_; } bool Component::should_warn_of_blocking(uint32_t blocking_time) { if (blocking_time > this->warn_if_blocking_over_) { @@ -201,7 +199,7 @@ bool Component::should_warn_of_blocking(uint32_t blocking_time) { return false; } void Component::mark_failed() { - ESP_LOGE(TAG, "%s was marked as failed", this->get_component_source()); + ESP_LOGE(TAG, "%s was marked as failed", LOG_STR_ARG(this->get_component_log_str())); this->set_component_state_(COMPONENT_STATE_FAILED); this->status_set_error(); // Also remove from loop since failed components shouldn't loop @@ -213,14 +211,14 @@ void Component::set_component_state_(uint8_t state) { } void Component::disable_loop() { if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { - ESP_LOGVV(TAG, "%s loop disabled", this->get_component_source()); + ESP_LOGVV(TAG, "%s loop disabled", LOG_STR_ARG(this->get_component_log_str())); this->set_component_state_(COMPONENT_STATE_LOOP_DONE); App.disable_component_loop_(this); } } void Component::enable_loop() { if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { - ESP_LOGVV(TAG, "%s loop enabled", this->get_component_source()); + ESP_LOGVV(TAG, "%s loop enabled", LOG_STR_ARG(this->get_component_log_str())); this->set_component_state_(COMPONENT_STATE_LOOP); App.enable_component_loop_(this); } @@ -240,7 +238,7 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() { } void Component::reset_to_construction_state() { if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { - ESP_LOGI(TAG, "%s is being reset to construction state", this->get_component_source()); + ESP_LOGI(TAG, "%s is being reset to construction state", LOG_STR_ARG(this->get_component_log_str())); this->set_component_state_(COMPONENT_STATE_CONSTRUCTION); // Clear error status when resetting this->status_clear_error(); @@ -286,14 +284,16 @@ void Component::status_set_warning(const char *message) { return; this->component_state_ |= STATUS_LED_WARNING; App.app_state_ |= STATUS_LED_WARNING; - ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message ? message : UNSPECIFIED_MESSAGE); + ESP_LOGW(TAG, "%s set Warning flag: %s", LOG_STR_ARG(this->get_component_log_str()), + message ? message : UNSPECIFIED_MESSAGE); } void Component::status_set_error(const char *message) { if ((this->component_state_ & STATUS_LED_ERROR) != 0) return; this->component_state_ |= STATUS_LED_ERROR; App.app_state_ |= STATUS_LED_ERROR; - ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message ? message : UNSPECIFIED_MESSAGE); + ESP_LOGE(TAG, "%s set Error flag: %s", LOG_STR_ARG(this->get_component_log_str()), + message ? message : UNSPECIFIED_MESSAGE); if (message != nullptr) { // Lazy allocate the error messages vector if needed if (!component_error_messages) { @@ -314,13 +314,13 @@ void Component::status_clear_warning() { if ((this->component_state_ & STATUS_LED_WARNING) == 0) return; this->component_state_ &= ~STATUS_LED_WARNING; - ESP_LOGW(TAG, "%s cleared Warning flag", this->get_component_source()); + ESP_LOGW(TAG, "%s cleared Warning flag", LOG_STR_ARG(this->get_component_log_str())); } void Component::status_clear_error() { if ((this->component_state_ & STATUS_LED_ERROR) == 0) return; this->component_state_ &= ~STATUS_LED_ERROR; - ESP_LOGE(TAG, "%s cleared Error flag", this->get_component_source()); + ESP_LOGE(TAG, "%s cleared Error flag", LOG_STR_ARG(this->get_component_log_str())); } void Component::status_momentary_warning(const std::string &name, uint32_t length) { this->status_set_warning(); @@ -419,8 +419,9 @@ uint32_t WarnIfComponentBlockingGuard::finish() { should_warn = blocking_time > WARN_IF_BLOCKING_OVER_MS; } if (should_warn) { - const char *src = component_ == nullptr ? "" : component_->get_component_source(); - ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time); + ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", + component_ == nullptr ? LOG_STR_LITERAL("") : LOG_STR_ARG(component_->get_component_log_str()), + blocking_time); ESP_LOGW(TAG, "Components should block for at most 30 ms"); } diff --git a/esphome/core/component.h b/esphome/core/component.h index 096c6f9c69..9a7e442c5c 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -5,6 +5,7 @@ #include #include +#include "esphome/core/log.h" #include "esphome/core/optional.h" namespace esphome { @@ -220,12 +221,12 @@ class Component { * * This is set by the ESPHome core, and should not be called manually. */ - void set_component_source(const char *source) { component_source_ = source; } - /** Get the integration where this component was declared as a string. + void set_component_source(const LogString *source) { component_source_ = source; } + /** Get the integration where this component was declared as a LogString for logging. * - * Returns "" if source not set + * Returns LOG_STR("") if source not set */ - const char *get_component_source() const; + const LogString *get_component_log_str() const; bool should_warn_of_blocking(uint32_t blocking_time); @@ -405,7 +406,7 @@ class Component { bool cancel_defer(const std::string &name); // NOLINT // Ordered for optimal packing on 32-bit systems - const char *component_source_{nullptr}; + const LogString *component_source_{nullptr}; uint16_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; ///< Warn if blocked for this many ms (max 65.5s) /// State of this component - each bit has a purpose: /// Bits 0-2: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED, 0x04=LOOP_DONE) diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index cdbccb753e..9b68f319d3 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -163,10 +163,10 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type // Debug logging const char *type_str = (type == SchedulerItem::TIMEOUT) ? "timeout" : "interval"; if (type == SchedulerItem::TIMEOUT) { - ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, item->get_source(), + ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, LOG_STR_ARG(item->get_source()), name_cstr ? name_cstr : "(null)", type_str, delay); } else { - ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(), + ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, LOG_STR_ARG(item->get_source()), name_cstr ? name_cstr : "(null)", type_str, delay, static_cast(item->get_next_execution() - now)); } @@ -385,7 +385,7 @@ void HOT Scheduler::call(uint32_t now) { const char *name = item->get_name(); bool is_cancelled = is_item_removed_(item.get()); ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64 "%s", - item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval, + item->get_type_str(), LOG_STR_ARG(item->get_source()), name ? name : "(null)", item->interval, item->get_next_execution() - now_64, item->get_next_execution(), is_cancelled ? " [CANCELLED]" : ""); old_items.push_back(std::move(item)); @@ -476,7 +476,7 @@ void HOT Scheduler::call(uint32_t now) { #ifdef ESPHOME_DEBUG_SCHEDULER const char *item_name = item->get_name(); ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")", - item->get_type_str(), item->get_source(), item_name ? item_name : "(null)", item->interval, + item->get_type_str(), LOG_STR_ARG(item->get_source()), item_name ? item_name : "(null)", item->interval, item->get_next_execution(), now_64); #endif /* ESPHOME_DEBUG_SCHEDULER */ diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 68ad64b9b1..301342e8c2 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -204,7 +204,7 @@ class Scheduler { next_execution_high_ = static_cast(value >> 32); } constexpr const char *get_type_str() const { return (type == TIMEOUT) ? "timeout" : "interval"; } - const char *get_source() const { return component ? component->get_component_source() : "unknown"; } + const LogString *get_source() const { return component ? component->get_component_log_str() : LOG_STR("unknown"); } }; // Common implementation for both timeout and interval diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 34e4eec1ee..291592dd2b 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -253,6 +253,19 @@ class StringLiteral(Literal): return cpp_string_escape(self.string) +class LogStringLiteral(Literal): + """A string literal that uses LOG_STR() macro for flash storage on ESP8266.""" + + __slots__ = ("string",) + + def __init__(self, string: str) -> None: + super().__init__() + self.string = string + + def __str__(self) -> str: + return f"LOG_STR({cpp_string_escape(self.string)})" + + class IntLiteral(Literal): __slots__ = ("i",) diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index b61b215bdc..2698b9b3d5 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -9,7 +9,7 @@ from esphome.const import ( ) from esphome.core import CORE, ID, coroutine from esphome.coroutine import FakeAwaitable -from esphome.cpp_generator import add, get_variable +from esphome.cpp_generator import LogStringLiteral, add, get_variable from esphome.cpp_types import App from esphome.types import ConfigFragmentType, ConfigType from esphome.util import Registry, RegistryEntry @@ -76,7 +76,7 @@ async def register_component(var, config): "Error while finding name of component, please report this", exc_info=e ) if name is not None: - add(var.set_component_source(name)) + add(var.set_component_source(LogStringLiteral(name))) add(App.register_component(var)) return var