[core] Deprecate status_set_error(const char*) and require LogString to prevent dangling pointers

This commit is contained in:
J. Nick Koston
2025-11-20 12:04:20 -06:00
parent a1e507baf8
commit 3955b66379
22 changed files with 81 additions and 50 deletions
@@ -87,7 +87,7 @@ void AbsoluteHumidityComponent::loop() {
break;
default:
this->publish_state(NAN);
this->status_set_error("Invalid saturation vapor pressure equation selection!");
this->status_set_error(LOG_STR("Invalid saturation vapor pressure equation selection!"));
return;
}
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
+1 -1
View File
@@ -83,7 +83,7 @@ void AHT10Component::setup() {
void AHT10Component::restart_read_() {
if (this->read_count_ == AHT10_ATTEMPTS) {
this->read_count_ = 0;
this->status_set_error("Reading timed out");
this->status_set_error(LOG_STR("Reading timed out"));
return;
}
this->read_count_++;
+1 -1
View File
@@ -8,7 +8,7 @@ Camera *Camera::global_camera = nullptr;
Camera::Camera() {
if (global_camera != nullptr) {
this->status_set_error("Multiple cameras are configured, but only one is supported.");
this->status_set_error(LOG_STR("Multiple cameras are configured, but only one is supported."));
this->mark_failed();
return;
}
@@ -19,13 +19,14 @@ void CST816Touchscreen::continue_setup_() {
case CST816T_CHIP_ID:
break;
default:
this->status_set_error(str_sprintf("Unknown chip ID 0x%02X", this->chip_id_).c_str());
ESP_LOGE(TAG, "Unknown chip ID: 0x%02X", this->chip_id_);
this->status_set_error(LOG_STR("Unknown chip ID"));
this->mark_failed();
return;
}
this->write_byte(REG_IRQ_CTL, IRQ_EN_MOTION);
} else if (!this->skip_probe_) {
this->status_set_error("Failed to read chip id");
this->status_set_error(LOG_STR("Failed to read chip id"));
this->mark_failed();
return;
}
@@ -88,7 +88,7 @@ void Esp32HostedUpdate::perform(bool force) {
hasher.add(this->firmware_data_, this->firmware_size_);
hasher.calculate();
if (!hasher.equals_bytes(this->firmware_sha256_.data())) {
this->status_set_error("SHA256 verification failed");
this->status_set_error(LOG_STR("SHA256 verification failed"));
this->publish_state();
return;
}
@@ -105,7 +105,7 @@ void Esp32HostedUpdate::perform(bool force) {
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to begin OTA: %s", esp_err_to_name(err));
this->state_ = prev_state;
this->status_set_error("Failed to begin OTA");
this->status_set_error(LOG_STR("Failed to begin OTA"));
this->publish_state();
return;
}
@@ -121,7 +121,7 @@ void Esp32HostedUpdate::perform(bool force) {
ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err));
esp_hosted_slave_ota_end(); // NOLINT
this->state_ = prev_state;
this->status_set_error("Failed to write OTA data");
this->status_set_error(LOG_STR("Failed to write OTA data"));
this->publish_state();
return;
}
@@ -134,7 +134,7 @@ void Esp32HostedUpdate::perform(bool force) {
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to end OTA: %s", esp_err_to_name(err));
this->state_ = prev_state;
this->status_set_error("Failed to end OTA");
this->status_set_error(LOG_STR("Failed to end OTA"));
this->publish_state();
return;
}
@@ -144,7 +144,7 @@ void Esp32HostedUpdate::perform(bool force) {
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to activate OTA: %s", esp_err_to_name(err));
this->state_ = prev_state;
this->status_set_error("Failed to activate OTA");
this->status_set_error(LOG_STR("Failed to activate OTA"));
this->publish_state();
return;
}
+3 -3
View File
@@ -36,20 +36,20 @@ void GDK101Component::setup() {
uint8_t data[2];
// first, reset the sensor
if (!this->reset_sensor_(data)) {
this->status_set_error("Reset failed!");
this->status_set_error(LOG_STR("Reset failed!"));
this->mark_failed();
return;
}
// sensor should acknowledge success of the reset procedure
if (data[0] != 1) {
this->status_set_error("Reset not acknowledged!");
this->status_set_error(LOG_STR("Reset not acknowledged!"));
this->mark_failed();
return;
}
delay(10);
// read firmware version
if (!this->read_fw_version_(data)) {
this->status_set_error("Failed to read firmware version");
this->status_set_error(LOG_STR("Failed to read firmware version"));
this->mark_failed();
return;
}
@@ -29,7 +29,7 @@ void HttpRequestUpdate::setup() {
this->publish_state();
} else if (state == ota::OTAState::OTA_ABORT || state == ota::OTAState::OTA_ERROR) {
this->state_ = update::UPDATE_STATE_AVAILABLE;
this->status_set_error("Failed to install firmware");
this->status_set_error(LOG_STR("Failed to install firmware"));
this->publish_state();
}
});
@@ -49,18 +49,19 @@ void HttpRequestUpdate::update_task(void *params) {
auto container = this_update->request_parent_->get(this_update->source_url_);
if (container == nullptr || container->status_code != HTTP_STATUS_OK) {
std::string msg = str_sprintf("Failed to fetch manifest from %s", this_update->source_url_.c_str());
ESP_LOGE(TAG, "Failed to fetch manifest from %s", this_update->source_url_.c_str());
// Defer to main loop to avoid race condition on component_state_ read-modify-write
this_update->defer([this_update, msg]() { this_update->status_set_error(msg.c_str()); });
this_update->defer([this_update]() { this_update->status_set_error(LOG_STR("Failed to fetch manifest")); });
UPDATE_RETURN;
}
RAMAllocator<uint8_t> allocator;
uint8_t *data = allocator.allocate(container->content_length);
if (data == nullptr) {
std::string msg = str_sprintf("Failed to allocate %zu bytes for manifest", container->content_length);
ESP_LOGE(TAG, "Failed to allocate %zu bytes for manifest", container->content_length);
// Defer to main loop to avoid race condition on component_state_ read-modify-write
this_update->defer([this_update, msg]() { this_update->status_set_error(msg.c_str()); });
this_update->defer(
[this_update]() { this_update->status_set_error(LOG_STR("Failed to allocate memory for manifest")); });
container->end();
UPDATE_RETURN;
}
@@ -121,9 +122,9 @@ void HttpRequestUpdate::update_task(void *params) {
}
if (!valid) {
std::string msg = str_sprintf("Failed to parse JSON from %s", this_update->source_url_.c_str());
ESP_LOGE(TAG, "Failed to parse JSON from %s", this_update->source_url_.c_str());
// Defer to main loop to avoid race condition on component_state_ read-modify-write
this_update->defer([this_update, msg]() { this_update->status_set_error(msg.c_str()); });
this_update->defer([this_update]() { this_update->status_set_error(LOG_STR("Failed to parse manifest JSON")); });
UPDATE_RETURN;
}
+2 -2
View File
@@ -466,7 +466,7 @@ void LvglComponent::setup() {
buffer = lv_custom_mem_alloc(buf_bytes); // NOLINT
}
if (buffer == nullptr) {
this->status_set_error("Memory allocation failure");
this->status_set_error(LOG_STR("Memory allocation failure"));
this->mark_failed();
return;
}
@@ -479,7 +479,7 @@ void LvglComponent::setup() {
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
if (this->rotate_buf_ == nullptr) {
this->status_set_error("Memory allocation failure");
this->status_set_error(LOG_STR("Memory allocation failure"));
this->mark_failed();
return;
}
+2 -2
View File
@@ -57,14 +57,14 @@ void MAX17043Component::setup() {
if (config_reg != MAX17043_CONFIG_POWER_UP_DEFAULT) {
ESP_LOGE(TAG, "Device does not appear to be a MAX17043");
this->status_set_error("unrecognised");
this->status_set_error(LOG_STR("unrecognised"));
this->mark_failed();
return;
}
// need to write back to config register to reset the sleep bit
if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT)) {
this->status_set_error("sleep reset failed");
this->status_set_error(LOG_STR("sleep reset failed"));
this->mark_failed();
return;
}
@@ -78,19 +78,20 @@ void SourceSpeaker::loop() {
} else {
switch (err) {
case ESP_ERR_NO_MEM:
this->status_set_error("Failed to start mixer: not enough memory");
this->status_set_error(LOG_STR("Failed to start mixer: not enough memory"));
break;
case ESP_ERR_NOT_SUPPORTED:
this->status_set_error("Failed to start mixer: unsupported bits per sample");
this->status_set_error(LOG_STR("Failed to start mixer: unsupported bits per sample"));
break;
case ESP_ERR_INVALID_ARG:
this->status_set_error("Failed to start mixer: audio stream isn't compatible with the other audio stream.");
this->status_set_error(
LOG_STR("Failed to start mixer: audio stream isn't compatible with the other audio stream."));
break;
case ESP_ERR_INVALID_STATE:
this->status_set_error("Failed to start mixer: mixer task failed to start");
this->status_set_error(LOG_STR("Failed to start mixer: mixer task failed to start"));
break;
default:
this->status_set_error("Failed to start mixer");
this->status_set_error(LOG_STR("Failed to start mixer"));
break;
}
@@ -317,7 +318,7 @@ void MixerSpeaker::loop() {
xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_STARTING);
}
if (event_group_bits & MixerEventGroupBits::ERR_ESP_NO_MEM) {
this->status_set_error("Failed to allocate the mixer's internal buffer");
this->status_set_error(LOG_STR("Failed to allocate the mixer's internal buffer"));
xEventGroupClearBits(this->event_group_, MixerEventGroupBits::ERR_ESP_NO_MEM);
}
if (event_group_bits & MixerEventGroupBits::STATE_RUNNING) {
+1 -1
View File
@@ -278,7 +278,7 @@ void NAU7802Sensor::loop() {
this->set_calibration_failure_(true);
this->state_ = CalibrationState::INACTIVE;
ESP_LOGE(TAG, "Failed to calibrate sensor");
this->status_set_error("Calibration Failed");
this->status_set_error(LOG_STR("Calibration Failed"));
return;
}
@@ -195,7 +195,7 @@ static void add(std::vector<uint8_t> &vec, const char *str) {
void PacketTransport::setup() {
this->name_ = App.get_name().c_str();
if (strlen(this->name_) > 255) {
this->status_set_error("Device name exceeds 255 chars");
this->status_set_error(LOG_STR("Device name exceeds 255 chars"));
this->mark_failed();
return;
}
@@ -66,17 +66,17 @@ void ResamplerSpeaker::loop() {
}
if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_NO_MEM) {
this->status_set_error("Resampler task failed to allocate the internal buffers");
this->status_set_error(LOG_STR("Resampler task failed to allocate the internal buffers"));
xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_NO_MEM);
this->state_ = speaker::STATE_STOPPING;
}
if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
this->status_set_error("Cannot resample due to an unsupported audio stream");
this->status_set_error(LOG_STR("Cannot resample due to an unsupported audio stream"));
xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED);
this->state_ = speaker::STATE_STOPPING;
}
if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_FAIL) {
this->status_set_error("Resampler task failed");
this->status_set_error(LOG_STR("Resampler task failed"));
xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_FAIL);
this->state_ = speaker::STATE_STOPPING;
}
@@ -106,12 +106,12 @@ void ResamplerSpeaker::loop() {
} else {
switch (err) {
case ESP_ERR_INVALID_STATE:
this->status_set_error("Failed to start resampler: resampler task failed to start");
this->status_set_error(LOG_STR("Failed to start resampler: resampler task failed to start"));
break;
case ESP_ERR_NO_MEM:
this->status_set_error("Failed to start resampler: not enough memory for task stack");
this->status_set_error(LOG_STR("Failed to start resampler: not enough memory for task stack"));
default:
this->status_set_error("Failed to start resampler");
this->status_set_error(LOG_STR("Failed to start resampler"));
break;
}
+1 -1
View File
@@ -13,7 +13,7 @@ void SHT4XComponent::start_heater_() {
ESP_LOGD(TAG, "Heater turning on");
if (this->write(cmd, 1) != i2c::ERROR_OK) {
this->status_set_error("Failed to turn on heater");
this->status_set_error(LOG_STR("Failed to turn on heater"));
}
}
+5 -5
View File
@@ -21,7 +21,7 @@ void UDPComponent::setup() {
if (this->should_broadcast_) {
this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (this->broadcast_socket_ == nullptr) {
this->status_set_error("Could not create socket");
this->status_set_error(LOG_STR("Could not create socket"));
this->mark_failed();
return;
}
@@ -41,14 +41,14 @@ void UDPComponent::setup() {
if (this->should_listen_) {
this->listen_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (this->listen_socket_ == nullptr) {
this->status_set_error("Could not create socket");
this->status_set_error(LOG_STR("Could not create socket"));
this->mark_failed();
return;
}
auto err = this->listen_socket_->setblocking(false);
if (err < 0) {
ESP_LOGE(TAG, "Unable to set nonblocking: errno %d", errno);
this->status_set_error("Unable to set nonblocking");
this->status_set_error(LOG_STR("Unable to set nonblocking"));
this->mark_failed();
return;
}
@@ -73,7 +73,7 @@ void UDPComponent::setup() {
err = this->listen_socket_->setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(imreq));
if (err < 0) {
ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
this->status_set_error("Failed to set IP_ADD_MEMBERSHIP");
this->status_set_error(LOG_STR("Failed to set IP_ADD_MEMBERSHIP"));
this->mark_failed();
return;
}
@@ -82,7 +82,7 @@ void UDPComponent::setup() {
err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
this->status_set_error("Unable to bind socket");
this->status_set_error(LOG_STR("Unable to bind socket"));
this->mark_failed();
return;
}
@@ -188,7 +188,7 @@ void USBClient::setup() {
auto err = usb_host_client_register(&config, &this->handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "client register failed: %s", esp_err_to_name(err));
this->status_set_error("Client register failed");
this->status_set_error(LOG_STR("Client register failed"));
this->mark_failed();
return;
}
@@ -11,7 +11,7 @@ void USBHost::setup() {
usb_host_config_t config{};
if (usb_host_install(&config) != ESP_OK) {
this->status_set_error("usb_host_install failed");
this->status_set_error(LOG_STR("usb_host_install failed"));
this->mark_failed();
return;
}
+2 -2
View File
@@ -320,7 +320,7 @@ static void fix_mps(const usb_ep_desc_t *ep) {
void USBUartTypeCdcAcm::on_connected() {
auto cdc_devs = this->parse_descriptors(this->device_handle_);
if (cdc_devs.empty()) {
this->status_set_error("No CDC-ACM device found");
this->status_set_error(LOG_STR("No CDC-ACM device found"));
this->disconnect();
return;
}
@@ -341,7 +341,7 @@ void USBUartTypeCdcAcm::on_connected() {
if (err != ESP_OK) {
ESP_LOGE(TAG, "usb_host_interface_claim failed: %s, channel=%d, intf=%d", esp_err_to_name(err), channel->index_,
channel->cdc_dev_.bulk_interface_number);
this->status_set_error("usb_host_interface_claim failed");
this->status_set_error(LOG_STR("usb_host_interface_claim failed"));
this->disconnect();
return;
}
@@ -206,7 +206,7 @@ void VoiceAssistant::loop() {
case State::START_MICROPHONE: {
ESP_LOGD(TAG, "Starting Microphone");
if (!this->allocate_buffers_()) {
this->status_set_error("Failed to allocate buffers");
this->status_set_error(LOG_STR("Failed to allocate buffers"));
return;
}
if (this->status_has_error()) {
@@ -67,7 +67,7 @@ void WakeOnLanButton::setup() {
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (this->broadcast_socket_ == nullptr) {
this->status_set_error("Could not create socket");
this->status_set_error(LOG_STR("Could not create socket"));
this->mark_failed();
return;
}
+25
View File
@@ -330,6 +330,31 @@ void Component::status_set_error(const char *message) {
component_error_messages->emplace_back(ComponentErrorMessage{this, message});
}
}
void Component::status_set_error(const LogString *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", LOG_STR_ARG(this->get_component_log_str()),
message ? LOG_STR_ARG(message) : LOG_STR_LITERAL("unspecified"));
if (message != nullptr) {
// Lazy allocate the error messages vector if needed
if (!component_error_messages) {
component_error_messages = std::make_unique<std::vector<ComponentErrorMessage>>();
}
// Store the LogString pointer directly (safe because LogString is always in flash/static memory)
const char *msg_ptr = LOG_STR_ARG(message);
// Check if this component already has an error message
for (auto &entry : *component_error_messages) {
if (entry.component == this) {
entry.message = msg_ptr;
return;
}
}
// Add new error message
component_error_messages->emplace_back(ComponentErrorMessage{this, msg_ptr});
}
}
void Component::status_clear_warning() {
if ((this->component_state_ & STATUS_LED_WARNING) == 0)
return;
+3
View File
@@ -216,7 +216,10 @@ class Component {
void status_set_warning(const char *message = nullptr);
void status_set_warning(const LogString *message);
// Remove before 2026.12.0
ESPDEPRECATED("Use status_set_error(LOG_STR(\"message\")) instead. Removed in 2026.12.0", "2025.6.0")
void status_set_error(const char *message = nullptr);
void status_set_error(const LogString *message);
void status_clear_warning();