mirror of
https://github.com/esphome/esphome.git
synced 2026-02-03 02:07:41 -07:00
Compare commits
8 Commits
integratio
...
_strtod_l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d5be4d677 | ||
|
|
d4d55df878 | ||
|
|
6af53f1dec | ||
|
|
dd44e3a560 | ||
|
|
6e52b7dbcf | ||
|
|
d12e2fea3d | ||
|
|
dfaabfc98a | ||
|
|
0f21465646 |
@@ -75,16 +75,16 @@ void ClimateCall::perform() {
|
||||
ESP_LOGD(TAG, " Swing: %s", LOG_STR_ARG(swing_mode_s));
|
||||
}
|
||||
if (this->target_temperature_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Temperature: %.2f", *this->target_temperature_);
|
||||
ESP_LOGD(TAG, " Target Temperature: %s%d.%02d", DECIMAL_2(*this->target_temperature_));
|
||||
}
|
||||
if (this->target_temperature_low_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Temperature Low: %.2f", *this->target_temperature_low_);
|
||||
ESP_LOGD(TAG, " Target Temperature Low: %s%d.%02d", DECIMAL_2(*this->target_temperature_low_));
|
||||
}
|
||||
if (this->target_temperature_high_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Temperature High: %.2f", *this->target_temperature_high_);
|
||||
ESP_LOGD(TAG, " Target Temperature High: %s%d.%02d", DECIMAL_2(*this->target_temperature_high_));
|
||||
}
|
||||
if (this->target_humidity_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Humidity: %.0f", *this->target_humidity_);
|
||||
ESP_LOGD(TAG, " Target Humidity: %d%%", (int) *this->target_humidity_);
|
||||
}
|
||||
this->parent_->control(*this);
|
||||
}
|
||||
@@ -161,7 +161,8 @@ void ClimateCall::validate_() {
|
||||
float low = *this->target_temperature_low_;
|
||||
float high = *this->target_temperature_high_;
|
||||
if (low > high) {
|
||||
ESP_LOGW(TAG, " Target temperature low %.2f must be less than target temperature high %.2f", low, high);
|
||||
ESP_LOGW(TAG, " Target temperature low %s%d.%02d must be less than high %s%d.%02d", DECIMAL_2(low),
|
||||
DECIMAL_2(high));
|
||||
this->target_temperature_low_.reset();
|
||||
this->target_temperature_high_.reset();
|
||||
}
|
||||
@@ -458,20 +459,20 @@ void Climate::publish_state() {
|
||||
ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode)));
|
||||
}
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) {
|
||||
ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature);
|
||||
ESP_LOGD(TAG, " Current Temperature: %s%d.%02d°C", DECIMAL_2(this->current_temperature));
|
||||
}
|
||||
if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE |
|
||||
CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) {
|
||||
ESP_LOGD(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low,
|
||||
this->target_temperature_high);
|
||||
ESP_LOGD(TAG, " Target Temperature: Low: %s%d.%02d°C High: %s%d.%02d°C", DECIMAL_2(this->target_temperature_low),
|
||||
DECIMAL_2(this->target_temperature_high));
|
||||
} else {
|
||||
ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature);
|
||||
ESP_LOGD(TAG, " Target Temperature: %s%d.%02d°C", DECIMAL_2(this->target_temperature));
|
||||
}
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) {
|
||||
ESP_LOGD(TAG, " Current Humidity: %.0f%%", this->current_humidity);
|
||||
ESP_LOGD(TAG, " Current Humidity: %d%%", (int) this->current_humidity);
|
||||
}
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) {
|
||||
ESP_LOGD(TAG, " Target Humidity: %.0f%%", this->target_humidity);
|
||||
ESP_LOGD(TAG, " Target Humidity: %d%%", (int) this->target_humidity);
|
||||
}
|
||||
|
||||
// Send state to frontend
|
||||
@@ -720,21 +721,21 @@ void Climate::dump_traits_(const char *tag) {
|
||||
ESP_LOGCONFIG(tag, "ClimateTraits:");
|
||||
ESP_LOGCONFIG(tag,
|
||||
" Visual settings:\n"
|
||||
" - Min temperature: %.1f\n"
|
||||
" - Max temperature: %.1f\n"
|
||||
" - Min temperature: %s%d.%d\n"
|
||||
" - Max temperature: %s%d.%d\n"
|
||||
" - Temperature step:\n"
|
||||
" Target: %.1f",
|
||||
traits.get_visual_min_temperature(), traits.get_visual_max_temperature(),
|
||||
traits.get_visual_target_temperature_step());
|
||||
" Target: %s%d.%d",
|
||||
DECIMAL_1(traits.get_visual_min_temperature()), DECIMAL_1(traits.get_visual_max_temperature()),
|
||||
DECIMAL_1(traits.get_visual_target_temperature_step()));
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) {
|
||||
ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step());
|
||||
ESP_LOGCONFIG(tag, " Current: %s%d.%d", DECIMAL_1(traits.get_visual_current_temperature_step()));
|
||||
}
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY |
|
||||
climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) {
|
||||
ESP_LOGCONFIG(tag,
|
||||
" - Min humidity: %.0f\n"
|
||||
" - Max humidity: %.0f",
|
||||
traits.get_visual_min_humidity(), traits.get_visual_max_humidity());
|
||||
" - Min humidity: %d\n"
|
||||
" - Max humidity: %d",
|
||||
(int) traits.get_visual_min_humidity(), (int) traits.get_visual_max_humidity());
|
||||
}
|
||||
if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE |
|
||||
CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) {
|
||||
|
||||
@@ -79,13 +79,13 @@ void CoverCall::perform() {
|
||||
}
|
||||
if (this->position_.has_value()) {
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGD(TAG, " Position: %.0f%%", *this->position_ * 100.0f);
|
||||
ESP_LOGD(TAG, " Position: %d%%", (int) (*this->position_ * 100.0f));
|
||||
} else {
|
||||
ESP_LOGD(TAG, " Command: %s", LOG_STR_ARG(cover_command_to_str(*this->position_)));
|
||||
}
|
||||
}
|
||||
if (this->tilt_.has_value()) {
|
||||
ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f);
|
||||
ESP_LOGD(TAG, " Tilt: %d%%", (int) (*this->tilt_ * 100.0f));
|
||||
}
|
||||
if (this->toggle_.has_value()) {
|
||||
ESP_LOGD(TAG, " Command: TOGGLE");
|
||||
@@ -105,7 +105,7 @@ void CoverCall::validate_() {
|
||||
ESP_LOGW(TAG, "'%s': position unsupported", name);
|
||||
this->position_.reset();
|
||||
} else if (pos < 0.0f || pos > 1.0f) {
|
||||
ESP_LOGW(TAG, "'%s': position %.2f out of range", name, pos);
|
||||
ESP_LOGW(TAG, "'%s': position %s%d.%02d out of range", name, DECIMAL_2(pos));
|
||||
this->position_ = clamp(pos, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ void CoverCall::validate_() {
|
||||
ESP_LOGW(TAG, "'%s': tilt unsupported", name);
|
||||
this->tilt_.reset();
|
||||
} else if (tilt < 0.0f || tilt > 1.0f) {
|
||||
ESP_LOGW(TAG, "'%s': tilt %.2f out of range", name, tilt);
|
||||
ESP_LOGW(TAG, "'%s': tilt %s%d.%02d out of range", name, DECIMAL_2(tilt));
|
||||
this->tilt_ = clamp(tilt, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ void Cover::publish_state(bool save) {
|
||||
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
|
||||
ESP_LOGD(TAG, " Position: %d%%", (int) (this->position * 100.0f));
|
||||
} else {
|
||||
if (this->position == COVER_OPEN) {
|
||||
ESP_LOGD(TAG, " State: OPEN");
|
||||
@@ -161,7 +161,7 @@ void Cover::publish_state(bool save) {
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
ESP_LOGD(TAG, " Tilt: %.0f%%", this->tilt * 100.0f);
|
||||
ESP_LOGD(TAG, " Tilt: %d%%", (int) (this->tilt * 100.0f));
|
||||
}
|
||||
ESP_LOGD(TAG, " Current Operation: %s", LOG_STR_ARG(cover_operation_to_str(this->current_operation)));
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ async def to_code(config):
|
||||
"pre:testing_mode.py",
|
||||
"pre:exclude_updater.py",
|
||||
"pre:exclude_waveform.py",
|
||||
"pre:remove_float_printf.py",
|
||||
"post:post_build.py",
|
||||
],
|
||||
)
|
||||
@@ -342,3 +343,8 @@ def copy_files() -> None:
|
||||
exclude_waveform_file,
|
||||
CORE.relative_build_path("exclude_waveform.py"),
|
||||
)
|
||||
remove_float_printf_file = dir / "remove_float_printf.py.script"
|
||||
copy_file_if_changed(
|
||||
remove_float_printf_file,
|
||||
CORE.relative_build_path("remove_float_printf.py"),
|
||||
)
|
||||
|
||||
45
esphome/components/esp8266/remove_float_printf.py.script
Normal file
45
esphome/components/esp8266/remove_float_printf.py.script
Normal file
@@ -0,0 +1,45 @@
|
||||
# pylint: disable=E0602
|
||||
Import("env") # noqa
|
||||
|
||||
# Remove float printf/scanf support from linker flags
|
||||
# The Arduino ESP8266 framework unconditionally adds:
|
||||
# -u _printf_float -u _scanf_float
|
||||
# This forces inclusion of float formatting code (~7KB) even when not used.
|
||||
#
|
||||
# ESPHome avoids %f format specifiers in logging to not require this code.
|
||||
# This script removes those flags to save flash space.
|
||||
#
|
||||
# Savings:
|
||||
# - _dtoa_r: ~3.4KB (double-to-ASCII conversion)
|
||||
# - _strtod_l: ~3.7KB (string-to-double conversion)
|
||||
# - _printf_float: ~1.3KB
|
||||
# - _scanf_float: ~1.3KB
|
||||
# - Additional float math helpers
|
||||
|
||||
|
||||
def remove_float_printf_flags(source, target, env):
|
||||
"""Remove -u _printf_float and -u _scanf_float from linker flags.
|
||||
|
||||
This is called as a pre-action before the link step.
|
||||
"""
|
||||
linkflags = env.get("LINKFLAGS", [])
|
||||
new_linkflags = []
|
||||
i = 0
|
||||
|
||||
while i < len(linkflags):
|
||||
flag = linkflags[i]
|
||||
if flag == "-u" and i + 1 < len(linkflags):
|
||||
next_flag = linkflags[i + 1]
|
||||
if next_flag in ("_printf_float", "_scanf_float"):
|
||||
print(f"ESPHome: Removing float printf support ({next_flag})")
|
||||
i += 2 # Skip both -u and the symbol
|
||||
continue
|
||||
new_linkflags.append(flag)
|
||||
i += 1
|
||||
|
||||
env.Replace(LINKFLAGS=new_linkflags)
|
||||
|
||||
|
||||
# Register the callback to run before the link step
|
||||
# This ensures it runs after the framework has added its flags
|
||||
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", remove_float_printf_flags)
|
||||
@@ -331,7 +331,7 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
if (now - last_progress > 1000) {
|
||||
last_progress = now;
|
||||
float percentage = (total * 100.0f) / ota_size;
|
||||
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
|
||||
ESP_LOGD(TAG, "Progress: %s%d.%d%%", DECIMAL_1(percentage));
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_IN_PROGRESS, percentage, 0);
|
||||
#endif
|
||||
|
||||
@@ -169,14 +169,14 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
if ((now - last_progress > 1000) or (container->get_bytes_read() == container->content_length)) {
|
||||
last_progress = now;
|
||||
float percentage = container->get_bytes_read() * 100.0f / container->content_length;
|
||||
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
|
||||
ESP_LOGD(TAG, "Progress: %s%d.%d%%", DECIMAL_1(percentage));
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
this->notify_state_(ota::OTA_IN_PROGRESS, percentage, 0);
|
||||
#endif
|
||||
}
|
||||
} // while
|
||||
|
||||
ESP_LOGI(TAG, "Done in %.0f seconds", float(millis() - update_start_time) / 1000);
|
||||
ESP_LOGI(TAG, "Done in %d seconds", (int) (float(millis() - update_start_time) / 1000));
|
||||
|
||||
// verify MD5 is as expected and act accordingly
|
||||
md5_receive.calculate();
|
||||
|
||||
@@ -13,7 +13,8 @@ static const char *const TAG = "light";
|
||||
static void clamp_and_log_if_invalid(const char *name, float &value, const LogString *param_name, float min = 0.0f,
|
||||
float max = 1.0f) {
|
||||
if (value < min || value > max) {
|
||||
ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, LOG_STR_ARG(param_name), value, min, max);
|
||||
ESP_LOGW(TAG, "'%s': %s value %s%d.%02d is out of range [%s%d.%02d - %s%d.%02d]", name, LOG_STR_ARG(param_name),
|
||||
DECIMAL_2(value), DECIMAL_2(min), DECIMAL_2(max));
|
||||
value = clamp(value, min, max);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +77,7 @@ static const LogString *color_mode_to_human(ColorMode color_mode) {
|
||||
// Helper to log percentage values
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
static void log_percent(const LogString *param, float value) {
|
||||
ESP_LOGD(TAG, " %s: %.0f%%", LOG_STR_ARG(param), value * 100.0f);
|
||||
ESP_LOGD(TAG, " %s: %d%%", LOG_STR_ARG(param), (int) (value * 100.0f));
|
||||
}
|
||||
#else
|
||||
#define log_percent(param, value)
|
||||
@@ -112,34 +113,34 @@ void LightCall::perform() {
|
||||
log_percent(LOG_STR("Color brightness"), v.get_color_brightness());
|
||||
}
|
||||
if (this->has_red() || this->has_green() || this->has_blue()) {
|
||||
ESP_LOGD(TAG, " Red: %.0f%%, Green: %.0f%%, Blue: %.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
|
||||
v.get_blue() * 100.0f);
|
||||
ESP_LOGD(TAG, " Red: %d%%, Green: %d%%, Blue: %d%%", (int) (v.get_red() * 100.0f),
|
||||
(int) (v.get_green() * 100.0f), (int) (v.get_blue() * 100.0f));
|
||||
}
|
||||
|
||||
if (this->has_white()) {
|
||||
log_percent(LOG_STR("White"), v.get_white());
|
||||
}
|
||||
if (this->has_color_temperature()) {
|
||||
ESP_LOGD(TAG, " Color temperature: %.1f mireds", v.get_color_temperature());
|
||||
ESP_LOGD(TAG, " Color temperature: %d mireds", (int) v.get_color_temperature());
|
||||
}
|
||||
|
||||
if (this->has_cold_white() || this->has_warm_white()) {
|
||||
ESP_LOGD(TAG, " Cold white: %.0f%%, warm white: %.0f%%", v.get_cold_white() * 100.0f,
|
||||
v.get_warm_white() * 100.0f);
|
||||
ESP_LOGD(TAG, " Cold white: %d%%, warm white: %d%%", (int) (v.get_cold_white() * 100.0f),
|
||||
(int) (v.get_warm_white() * 100.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->has_flash_()) {
|
||||
// FLASH
|
||||
if (publish) {
|
||||
ESP_LOGD(TAG, " Flash length: %.1fs", this->flash_length_ / 1e3f);
|
||||
ESP_LOGD(TAG, " Flash length: %u ms", this->flash_length_);
|
||||
}
|
||||
|
||||
this->parent_->start_flash_(v, this->flash_length_, publish);
|
||||
} else if (this->has_transition_()) {
|
||||
// TRANSITION
|
||||
if (publish) {
|
||||
ESP_LOGD(TAG, " Transition length: %.1fs", this->transition_length_ / 1e3f);
|
||||
ESP_LOGD(TAG, " Transition length: %u ms", this->transition_length_);
|
||||
}
|
||||
|
||||
// Special case: Transition and effect can be set when turning off
|
||||
|
||||
@@ -92,15 +92,15 @@ void LightState::dump_config() {
|
||||
auto traits = this->get_traits();
|
||||
if (traits.supports_color_capability(ColorCapability::BRIGHTNESS)) {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Default Transition Length: %.1fs\n"
|
||||
" Gamma Correct: %.2f",
|
||||
this->default_transition_length_ / 1e3f, this->gamma_correct_);
|
||||
" Default Transition Length: %u ms\n"
|
||||
" Gamma Correct: %s%d.%02d",
|
||||
this->default_transition_length_, DECIMAL_2(this->gamma_correct_));
|
||||
}
|
||||
if (traits.supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Min Mireds: %.1f\n"
|
||||
" Max Mireds: %.1f",
|
||||
traits.get_min_mireds(), traits.get_max_mireds());
|
||||
" Min Mireds: %d\n"
|
||||
" Max Mireds: %d",
|
||||
(int) traits.get_min_mireds(), (int) traits.get_max_mireds());
|
||||
}
|
||||
}
|
||||
void LightState::loop() {
|
||||
|
||||
@@ -91,7 +91,7 @@ void MediaPlayerCall::perform() {
|
||||
ESP_LOGD(TAG, " Media URL: %s", this->media_url_.value().c_str());
|
||||
}
|
||||
if (this->volume_.has_value()) {
|
||||
ESP_LOGD(TAG, " Volume: %.2f", this->volume_.value());
|
||||
ESP_LOGD(TAG, " Volume: %d%%", (int) (this->volume_.value() * 100.0f));
|
||||
}
|
||||
if (this->announcement_.has_value()) {
|
||||
ESP_LOGD(TAG, " Announcement: %s", this->announcement_.value() ? "yes" : "no");
|
||||
|
||||
@@ -22,7 +22,7 @@ void log_number(const char *tag, const char *prefix, const char *type, Number *o
|
||||
void Number::publish_state(float state) {
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s' >> %.2f", this->get_name().c_str(), state);
|
||||
ESP_LOGD(TAG, "'%s' >> %s%d.%02d", this->get_name().c_str(), DECIMAL_2(state));
|
||||
this->state_callback_.call(state);
|
||||
#if defined(USE_NUMBER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_number_update(this);
|
||||
|
||||
@@ -13,8 +13,8 @@ void NumberCall::log_perform_warning_(const LogString *message) {
|
||||
|
||||
void NumberCall::log_perform_warning_value_range_(const LogString *comparison, const LogString *limit_type, float val,
|
||||
float limit) {
|
||||
ESP_LOGW(TAG, "'%s': %f %s %s %f", this->parent_->get_name().c_str(), val, LOG_STR_ARG(comparison),
|
||||
LOG_STR_ARG(limit_type), limit);
|
||||
ESP_LOGW(TAG, "'%s': %s%d.%02d %s %s %s%d.%02d", this->parent_->get_name().c_str(), DECIMAL_2(val),
|
||||
LOG_STR_ARG(comparison), LOG_STR_ARG(limit_type), DECIMAL_2(limit));
|
||||
}
|
||||
|
||||
NumberCall &NumberCall::set_value(float value) { return this->with_operation(NUMBER_OP_SET).with_value(value); }
|
||||
@@ -120,7 +120,7 @@ void NumberCall::perform() {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, " New value: %f", target_value);
|
||||
ESP_LOGD(TAG, " New value: %s%d.%02d", DECIMAL_2(target_value));
|
||||
this->parent_->control(target_value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "binary_output.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -9,10 +10,10 @@ namespace output {
|
||||
#define LOG_FLOAT_OUTPUT(this) \
|
||||
LOG_BINARY_OUTPUT(this) \
|
||||
if (this->max_power_ != 1.0f) { \
|
||||
ESP_LOGCONFIG(TAG, " Max Power: %.1f%%", this->max_power_ * 100.0f); \
|
||||
ESP_LOGCONFIG(TAG, " Max Power: %s%d.%d%%", DECIMAL_1(this->max_power_ * 100.0f)); \
|
||||
} \
|
||||
if (this->min_power_ != 0.0f) { \
|
||||
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->min_power_ * 100.0f); \
|
||||
ESP_LOGCONFIG(TAG, " Min Power: %s%d.%d%%", DECIMAL_1(this->min_power_ * 100.0f)); \
|
||||
}
|
||||
|
||||
/** Base class for all output components that can output a variable level, like PWM.
|
||||
|
||||
@@ -66,7 +66,7 @@ void Sensor::publish_state(float state) {
|
||||
this->raw_state = state;
|
||||
this->raw_callback_.call(state);
|
||||
|
||||
ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state);
|
||||
ESP_LOGV(TAG, "'%s': Received new state %s%d.%02d", this->name_.c_str(), DECIMAL_2(state));
|
||||
|
||||
if (this->filter_list_ == nullptr) {
|
||||
this->internal_send_state_to_frontend(state);
|
||||
@@ -115,8 +115,20 @@ float Sensor::get_raw_state() const { return this->raw_state; }
|
||||
void Sensor::internal_send_state_to_frontend(float state) {
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s' >> %.*f %s", this->get_name().c_str(), std::max(0, (int) this->get_accuracy_decimals()), state,
|
||||
this->get_unit_of_measurement_ref().c_str());
|
||||
// Use integer formatting to avoid pulling in _dtoa_r (~3.4KB)
|
||||
// Format based on accuracy_decimals: 0 = integer, 1 = 1 decimal, 2+ = 2 decimals
|
||||
int decimals = std::max(0, (int) this->get_accuracy_decimals());
|
||||
if (decimals == 0) {
|
||||
ESP_LOGD(TAG, "'%s' >> %d %s", this->get_name().c_str(), (int) state, this->get_unit_of_measurement_ref().c_str());
|
||||
} else if (decimals == 1) {
|
||||
int scaled = static_cast<int>(state * 10.0f);
|
||||
ESP_LOGD(TAG, "'%s' >> %s%d.%d %s", this->get_name().c_str(), scaled < 0 ? "-" : "", std::abs(scaled / 10),
|
||||
std::abs(scaled % 10), this->get_unit_of_measurement_ref().c_str());
|
||||
} else {
|
||||
int scaled = static_cast<int>(state * 100.0f);
|
||||
ESP_LOGD(TAG, "'%s' >> %s%d.%02d %s", this->get_name().c_str(), scaled < 0 ? "-" : "", std::abs(scaled / 100),
|
||||
std::abs(scaled % 100), this->get_unit_of_measurement_ref().c_str());
|
||||
}
|
||||
this->callback_.call(state);
|
||||
#if defined(USE_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_sensor_update(this);
|
||||
|
||||
@@ -30,7 +30,7 @@ void UpdateEntity::publish_state() {
|
||||
}
|
||||
|
||||
if (this->update_info_.has_progress) {
|
||||
ESP_LOGD(TAG, " Progress: %.0f%%", this->update_info_.progress);
|
||||
ESP_LOGD(TAG, " Progress: %d%%", (int) this->update_info_.progress);
|
||||
}
|
||||
|
||||
this->set_has_state(true);
|
||||
|
||||
@@ -76,7 +76,7 @@ void ValveCall::perform() {
|
||||
}
|
||||
if (this->position_.has_value()) {
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGD(TAG, " Position: %.0f%%", *this->position_ * 100.0f);
|
||||
ESP_LOGD(TAG, " Position: %d%%", (int) (*this->position_ * 100.0f));
|
||||
} else {
|
||||
ESP_LOGD(TAG, " Command: %s", LOG_STR_ARG(valve_command_to_str(*this->position_)));
|
||||
}
|
||||
@@ -96,7 +96,8 @@ void ValveCall::validate_() {
|
||||
ESP_LOGW(TAG, "'%s' - This valve device does not support setting position!", this->parent_->get_name().c_str());
|
||||
this->position_.reset();
|
||||
} else if (pos < 0.0f || pos > 1.0f) {
|
||||
ESP_LOGW(TAG, "'%s' - Position %.2f is out of range [0.0 - 1.0]", this->parent_->get_name().c_str(), pos);
|
||||
ESP_LOGW(TAG, "'%s' - Position %s%d.%02d is out of range [0.0 - 1.0]", this->parent_->get_name().c_str(),
|
||||
DECIMAL_2(pos));
|
||||
this->position_ = clamp(pos, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -132,7 +133,7 @@ void Valve::publish_state(bool save) {
|
||||
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
|
||||
ESP_LOGD(TAG, " Position: %d%%", (int) (this->position * 100.0f));
|
||||
} else {
|
||||
if (this->position == VALVE_OPEN) {
|
||||
ESP_LOGD(TAG, " State: OPEN");
|
||||
|
||||
@@ -84,7 +84,7 @@ void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {
|
||||
// access to the actual firmware size until the upload is complete. This is intentional
|
||||
// as it still gives the user a reasonable progress indication.
|
||||
percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
|
||||
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
|
||||
ESP_LOGD(TAG, "OTA in progress: %s%d.%d%%", DECIMAL_1(percentage));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "OTA in progress: %" PRIu32 " bytes read", this->ota_read_length_);
|
||||
}
|
||||
|
||||
@@ -450,9 +450,12 @@ void log_update_interval(const char *tag, PollingComponent *component) {
|
||||
if (update_interval == SCHEDULER_DONT_RUN) {
|
||||
ESP_LOGCONFIG(tag, " Update Interval: never");
|
||||
} else if (update_interval < 100) {
|
||||
ESP_LOGCONFIG(tag, " Update Interval: %.3fs", update_interval / 1000.0f);
|
||||
// Use integer math to avoid pulling in _dtoa_r (~3.4KB)
|
||||
// update_interval is in ms, display as X.YYYs
|
||||
ESP_LOGCONFIG(tag, " Update Interval: %d.%03ds", update_interval / 1000, update_interval % 1000);
|
||||
} else {
|
||||
ESP_LOGCONFIG(tag, " Update Interval: %.1fs", update_interval / 1000.0f);
|
||||
// Display as X.Ys (1 decimal place)
|
||||
ESP_LOGCONFIG(tag, " Update Interval: %d.%ds", update_interval / 1000, (update_interval % 1000) / 100);
|
||||
}
|
||||
}
|
||||
float Component::get_actual_setup_priority() const {
|
||||
|
||||
@@ -478,6 +478,44 @@ static inline void normalize_accuracy_decimals(float &value, int8_t &accuracy_de
|
||||
}
|
||||
}
|
||||
|
||||
// Power of 10 lookup table for accuracy_decimals 0-9
|
||||
static const uint32_t POWERS_OF_10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
|
||||
|
||||
// Format float to buffer without using %f (avoids _dtoa_r ~3.4KB)
|
||||
static size_t format_float_to_buf(char *buf, size_t buf_size, float value, int8_t accuracy_decimals) {
|
||||
if (buf_size == 0)
|
||||
return 0;
|
||||
|
||||
// Handle sign
|
||||
const char *sign = "";
|
||||
if (value < 0) {
|
||||
sign = "-";
|
||||
value = -value;
|
||||
}
|
||||
|
||||
// Clamp accuracy_decimals to supported range
|
||||
if (accuracy_decimals > 9)
|
||||
accuracy_decimals = 9;
|
||||
|
||||
int len;
|
||||
if (accuracy_decimals == 0) {
|
||||
// Integer only
|
||||
len = snprintf(buf, buf_size, "%s%d", sign, static_cast<int>(value + 0.5f));
|
||||
} else {
|
||||
// Scale and round
|
||||
uint32_t divisor = POWERS_OF_10[accuracy_decimals];
|
||||
uint64_t scaled = static_cast<uint64_t>(value * divisor + 0.5f);
|
||||
uint32_t integer_part = scaled / divisor;
|
||||
uint32_t decimal_part = scaled % divisor;
|
||||
// %0*d pads with leading zeros to match accuracy_decimals
|
||||
len = snprintf(buf, buf_size, "%s%u.%0*u", sign, integer_part, accuracy_decimals, decimal_part);
|
||||
}
|
||||
|
||||
if (len < 0)
|
||||
return 0;
|
||||
return static_cast<size_t>(len) >= buf_size ? buf_size - 1 : static_cast<size_t>(len);
|
||||
}
|
||||
|
||||
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
|
||||
char buf[VALUE_ACCURACY_MAX_LEN];
|
||||
value_accuracy_to_buf(buf, value, accuracy_decimals);
|
||||
@@ -486,12 +524,7 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
|
||||
|
||||
size_t value_accuracy_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value, int8_t accuracy_decimals) {
|
||||
normalize_accuracy_decimals(value, accuracy_decimals);
|
||||
// snprintf returns chars that would be written (excluding null), or negative on error
|
||||
int len = snprintf(buf.data(), buf.size(), "%.*f", accuracy_decimals, value);
|
||||
if (len < 0)
|
||||
return 0; // encoding error
|
||||
// On truncation, snprintf returns would-be length; actual written is buf.size() - 1
|
||||
return static_cast<size_t>(len) >= buf.size() ? buf.size() - 1 : static_cast<size_t>(len);
|
||||
return format_float_to_buf(buf.data(), buf.size(), value, accuracy_decimals);
|
||||
}
|
||||
|
||||
size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value,
|
||||
@@ -500,25 +533,33 @@ size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> bu
|
||||
return value_accuracy_to_buf(buf, value, accuracy_decimals);
|
||||
}
|
||||
normalize_accuracy_decimals(value, accuracy_decimals);
|
||||
// snprintf returns chars that would be written (excluding null), or negative on error
|
||||
int len = snprintf(buf.data(), buf.size(), "%.*f %s", accuracy_decimals, value, unit_of_measurement.c_str());
|
||||
if (len < 0)
|
||||
return 0; // encoding error
|
||||
// On truncation, snprintf returns would-be length; actual written is buf.size() - 1
|
||||
return static_cast<size_t>(len) >= buf.size() ? buf.size() - 1 : static_cast<size_t>(len);
|
||||
// Format value first, then append unit
|
||||
size_t len = format_float_to_buf(buf.data(), buf.size(), value, accuracy_decimals);
|
||||
if (len + 1 < buf.size()) {
|
||||
// Append space and unit
|
||||
int uom_len = snprintf(buf.data() + len, buf.size() - len, " %s", unit_of_measurement.c_str());
|
||||
if (uom_len > 0) {
|
||||
len += static_cast<size_t>(uom_len);
|
||||
if (len >= buf.size())
|
||||
len = buf.size() - 1;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int8_t step_to_accuracy_decimals(float step) {
|
||||
// use printf %g to find number of digits based on temperature step
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%.5g", step);
|
||||
|
||||
std::string str{buf};
|
||||
size_t dot_pos = str.find('.');
|
||||
if (dot_pos == std::string::npos)
|
||||
// Determine decimal places needed without using %g (avoids _dtoa_r)
|
||||
if (step >= 1.0f)
|
||||
return 0;
|
||||
|
||||
return str.length() - dot_pos - 1;
|
||||
// Multiply by powers of 10 until we get an integer
|
||||
for (int8_t decimals = 1; decimals <= 5; decimals++) {
|
||||
float scaled = step * POWERS_OF_10[decimals];
|
||||
// Check if scaled is close to an integer (within floating point tolerance)
|
||||
if (std::abs(scaled - std::round(scaled)) < 0.001f)
|
||||
return decimals;
|
||||
}
|
||||
return 5; // Max 5 decimal places
|
||||
}
|
||||
|
||||
// Store BASE64 characters as array - automatically placed in flash/ROM on embedded platforms
|
||||
|
||||
@@ -1917,4 +1917,45 @@ class CompactString {
|
||||
|
||||
static_assert(sizeof(CompactString) == 20, "CompactString must be exactly 20 bytes");
|
||||
|
||||
/// @name Float formatting helpers (avoid _dtoa_r bloat)
|
||||
/// These helpers format floats as fixed-point integers for logging without pulling in
|
||||
/// the ~3.4KB _dtoa_r library. They handle negative values correctly including -0.x cases.
|
||||
/// Usage: ESP_LOGD(TAG, "Temp: %s%d.%d°C", DECIMAL_1(temp));
|
||||
/// Usage: ESP_LOGD(TAG, "Value: %s%d.%02d", DECIMAL_2(value));
|
||||
///@{
|
||||
|
||||
/// Helper for formatting floats with 1 decimal place
|
||||
struct Decimal1 {
|
||||
const char *sign;
|
||||
int integer;
|
||||
int decimal;
|
||||
explicit Decimal1(float value) {
|
||||
int scaled = static_cast<int>(value * 10.0f);
|
||||
sign = scaled < 0 ? "-" : "";
|
||||
integer = std::abs(scaled / 10);
|
||||
decimal = std::abs(scaled % 10);
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper for formatting floats with 2 decimal places
|
||||
struct Decimal2 {
|
||||
const char *sign;
|
||||
int integer;
|
||||
int decimal;
|
||||
explicit Decimal2(float value) {
|
||||
int scaled = static_cast<int>(value * 100.0f);
|
||||
sign = scaled < 0 ? "-" : "";
|
||||
integer = std::abs(scaled / 100);
|
||||
decimal = std::abs(scaled % 100);
|
||||
}
|
||||
};
|
||||
|
||||
/// Format float with 1 decimal place - expands to 3 args for %s%d.%d
|
||||
#define DECIMAL_1(v) esphome::Decimal1(v).sign, esphome::Decimal1(v).integer, esphome::Decimal1(v).decimal
|
||||
|
||||
/// Format float with 2 decimal places - expands to 3 args for %s%d.%02d
|
||||
#define DECIMAL_2(v) esphome::Decimal2(v).sign, esphome::Decimal2(v).integer, esphome::Decimal2(v).decimal
|
||||
|
||||
///@}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
@@ -296,14 +296,14 @@ void HOT Scheduler::set_retry_common_(Component *component, NameType name_type,
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
{
|
||||
SchedulerNameLog name_log;
|
||||
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
|
||||
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%s%d.%d)",
|
||||
name_log.format(name_type, static_name, hash_or_id), initial_wait_time, max_attempts,
|
||||
backoff_increase_factor);
|
||||
DECIMAL_1(backoff_increase_factor));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (backoff_increase_factor < 0.0001) {
|
||||
ESP_LOGE(TAG, "set_retry: backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor,
|
||||
ESP_LOGE(TAG, "set_retry: backoff_factor %s%d.%d too small, using 1.0: %s", DECIMAL_1(backoff_increase_factor),
|
||||
(name_type == NameType::STATIC_STRING && static_name) ? static_name : "");
|
||||
backoff_increase_factor = 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user