diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 4478c93f26..0567900b06 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -130,9 +130,8 @@ void APIConnection::start() { return; } // Initialize client name with peername (IP address) until Hello message provides actual name - char peername[socket::PEERNAME_MAX_LEN]; - size_t len = this->helper_->getpeername_to(peername); - this->helper_->set_client_name(peername, len); + const char *peername = this->helper_->get_client_peername(); + this->helper_->set_client_name(peername, strlen(peername)); } APIConnection::~APIConnection() { @@ -1505,10 +1504,8 @@ void APIConnection::complete_authentication_() { this->flags_.connection_state = static_cast(ConnectionState::AUTHENTICATED); this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("connected")); #ifdef USE_API_CLIENT_CONNECTED_TRIGGER - char peername_buf[socket::PEERNAME_MAX_LEN]; - this->helper_->getpeername_to(peername_buf); this->parent_->get_client_connected_trigger()->trigger(std::string(this->helper_->get_client_name()), - std::string(peername_buf)); + std::string(this->helper_->get_client_peername())); #endif #ifdef USE_HOMEASSISTANT_TIME if (homeassistant::global_homeassistant_time != nullptr) { @@ -1527,10 +1524,8 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) { this->helper_->set_client_name(msg.client_info.c_str(), msg.client_info.size()); this->client_api_version_major_ = msg.api_version_major; this->client_api_version_minor_ = msg.api_version_minor; - char peername[socket::PEERNAME_MAX_LEN]; - this->helper_->getpeername_to(peername); ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->helper_->get_client_name(), - peername, this->client_api_version_major_, this->client_api_version_minor_); + this->helper_->get_client_peername(), this->client_api_version_major_, this->client_api_version_minor_); HelloResponse resp; resp.api_version_major = 1; @@ -2089,17 +2084,13 @@ void APIConnection::process_state_subscriptions_() { #endif // USE_API_HOMEASSISTANT_STATES void APIConnection::log_client_(int level, const LogString *message) { - char peername[socket::PEERNAME_MAX_LEN]; - this->helper_->getpeername_to(peername); - esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->helper_->get_client_name(), peername, - LOG_STR_ARG(message)); + esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->helper_->get_client_name(), + this->helper_->get_client_peername(), LOG_STR_ARG(message)); } void APIConnection::log_warning_(const LogString *message, APIError err) { - char peername[socket::PEERNAME_MAX_LEN]; - this->helper_->getpeername_to(peername); - ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->helper_->get_client_name(), peername, LOG_STR_ARG(message), - LOG_STR_ARG(api_error_to_logstr(err)), errno); + ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->helper_->get_client_name(), this->helper_->get_client_peername(), + LOG_STR_ARG(message), LOG_STR_ARG(api_error_to_logstr(err)), errno); } } // namespace esphome::api diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 16072d0751..58644ca172 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -280,10 +280,8 @@ class APIConnection final : public APIServerConnection { bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override; const char *get_name() const { return this->helper_->get_client_name(); } - /// Get peer name (IP address) into a stack buffer - avoids heap allocation - size_t get_peername_to(std::span buf) const { - return this->helper_->getpeername_to(buf); - } + /// Get peer name (IP address) - cached at connection init time + const char *get_peername() const { return this->helper_->get_client_peername(); } protected: // Helper function to handle authentication completion diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 373d27260a..dd44fe9e17 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -16,12 +16,7 @@ static const char *const TAG = "api.frame_helper"; static constexpr size_t API_MAX_LOG_BYTES = 168; #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE -#define HELPER_LOG(msg, ...) \ - do { \ - char peername__[socket::PEERNAME_MAX_LEN]; \ - this->socket_->getpeername_to(peername__); \ - ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \ - } while (0) +#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, this->client_peername_, ##__VA_ARGS__) #else #define HELPER_LOG(msg, ...) ((void) 0) #endif @@ -250,6 +245,8 @@ APIError APIFrameHelper::init_common_() { HELPER_LOG("Bad state for init %d", (int) state_); return APIError::BAD_STATE; } + // Cache peername now while socket is valid - needed for error logging after socket failure + this->socket_->getpeername_to(this->client_peername_); int err = this->socket_->setblocking(false); if (err != 0) { state_ = State::FAILED; diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 37e972e205..2364aca4ed 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -86,6 +86,8 @@ class APIFrameHelper { // Get client name (null-terminated) const char *get_client_name() const { return this->client_name_; } + // Get client peername/IP (null-terminated, cached at init time for availability after socket failure) + const char *get_client_peername() const { return this->client_peername_; } // Set client name from buffer with length (truncates if needed) void set_client_name(const char *name, size_t len) { size_t copy_len = std::min(len, sizeof(this->client_name_) - 1); @@ -98,7 +100,6 @@ class APIFrameHelper { virtual APIError read_packet(ReadPacketBuffer *buffer) = 0; bool can_write_without_blocking() { return this->state_ == State::DATA && this->tx_buf_count_ == 0; } int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); } - size_t getpeername_to(std::span buf) { return socket_->getpeername_to(buf); } APIError close() { state_ = State::CLOSED; int err = this->socket_->close(); @@ -199,6 +200,8 @@ class APIFrameHelper { // Client name buffer - stores name from Hello message or initial peername char client_name_[CLIENT_INFO_NAME_MAX_LEN]{}; + // Cached peername/IP address - captured at init time for availability after socket failure + char client_peername_[socket::PEERNAME_MAX_LEN]{}; // Group smaller types together uint16_t rx_buf_len_ = 0; diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index 11a399feab..186f0428c7 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -28,12 +28,7 @@ static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit") static constexpr size_t API_MAX_LOG_BYTES = 168; #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE -#define HELPER_LOG(msg, ...) \ - do { \ - char peername__[socket::PEERNAME_MAX_LEN]; \ - this->socket_->getpeername_to(peername__); \ - ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \ - } while (0) +#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, this->client_peername_, ##__VA_ARGS__) #else #define HELPER_LOG(msg, ...) ((void) 0) #endif diff --git a/esphome/components/api/api_frame_helper_plaintext.cpp b/esphome/components/api/api_frame_helper_plaintext.cpp index 8629ca71e4..5d1fac54c8 100644 --- a/esphome/components/api/api_frame_helper_plaintext.cpp +++ b/esphome/components/api/api_frame_helper_plaintext.cpp @@ -21,12 +21,7 @@ static const char *const TAG = "api.plaintext"; static constexpr size_t API_MAX_LOG_BYTES = 168; #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE -#define HELPER_LOG(msg, ...) \ - do { \ - char peername__[socket::PEERNAME_MAX_LEN]; \ - this->socket_->getpeername_to(peername__); \ - ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \ - } while (0) +#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, this->client_peername_, ##__VA_ARGS__) #else #define HELPER_LOG(msg, ...) ((void) 0) #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 23178a1243..4ea64dcbab 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -187,9 +187,7 @@ void APIServer::loop() { // Rare case: handle disconnection #ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER - char peername_buf[socket::PEERNAME_MAX_LEN]; - client->get_peername_to(peername_buf); - this->client_disconnected_trigger_->trigger(std::string(client->get_name()), std::string(peername_buf)); + this->client_disconnected_trigger_->trigger(std::string(client->get_name()), std::string(client->get_peername())); #endif #ifdef USE_API_USER_DEFINED_ACTION_RESPONSES this->unregister_active_action_calls_for_connection(client.get()); diff --git a/esphome/components/cap1188/cap1188.cpp b/esphome/components/cap1188/cap1188.cpp index 683e5cf487..9e8c87d147 100644 --- a/esphome/components/cap1188/cap1188.cpp +++ b/esphome/components/cap1188/cap1188.cpp @@ -63,14 +63,14 @@ void CAP1188Component::finish_setup_() { } void CAP1188Component::dump_config() { - ESP_LOGCONFIG(TAG, "CAP1188:"); - LOG_I2C_DEVICE(this); - LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, + "CAP1188:\n" " Product ID: 0x%x\n" " Manufacture ID: 0x%x\n" " Revision ID: 0x%x", this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_); + LOG_I2C_DEVICE(this); + LOG_PIN(" Reset Pin: ", this->reset_pin_); switch (this->error_code_) { case COMMUNICATION_FAILED: diff --git a/esphome/components/chsc6x/chsc6x_touchscreen.cpp b/esphome/components/chsc6x/chsc6x_touchscreen.cpp index 31c9466691..941144e451 100644 --- a/esphome/components/chsc6x/chsc6x_touchscreen.cpp +++ b/esphome/components/chsc6x/chsc6x_touchscreen.cpp @@ -32,14 +32,14 @@ void CHSC6XTouchscreen::update_touches() { } void CHSC6XTouchscreen::dump_config() { - ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:"); - LOG_I2C_DEVICE(this); - LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); ESP_LOGCONFIG(TAG, + "CHSC6X Touchscreen:\n" " Touch timeout: %d\n" " x_raw_max_: %d\n" " y_raw_max_: %d", this->touch_timeout_, this->x_raw_max_, this->y_raw_max_); + LOG_I2C_DEVICE(this); + LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); } } // namespace chsc6x diff --git a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp index 5be93692c0..d18d4e7c94 100644 --- a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +++ b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp @@ -83,14 +83,14 @@ void CST816Touchscreen::update_touches() { } void CST816Touchscreen::dump_config() { - ESP_LOGCONFIG(TAG, "CST816 Touchscreen:"); - LOG_I2C_DEVICE(this); - LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); - LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, + "CST816 Touchscreen:\n" " X Raw Min: %d, X Raw Max: %d\n" " Y Raw Min: %d, Y Raw Max: %d", this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_); + LOG_I2C_DEVICE(this); + LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); const char *name; switch (this->chip_id_) { case CST716_CHIP_ID: diff --git a/esphome/components/debug/debug_esp8266.cpp b/esphome/components/debug/debug_esp8266.cpp index 3395d9db12..7427b32290 100644 --- a/esphome/components/debug/debug_esp8266.cpp +++ b/esphome/components/debug/debug_esp8266.cpp @@ -47,14 +47,17 @@ void DebugComponent::get_device_info_(std::string &device_info) { #if !defined(CLANG_TIDY) auto reset_reason = get_reset_reason_(); - ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId()); - ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion()); - ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str()); - ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode()); - ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz()); - ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId()); - ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); - ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str()); + ESP_LOGD(TAG, + "Chip ID: 0x%08X\n" + "SDK Version: %s\n" + "Core Version: %s\n" + "Boot Version=%u Mode=%u\n" + "CPU Frequency: %u\n" + "Flash Chip ID=0x%08X\n" + "Reset Reason: %s\n" + "Reset Info: %s", + ESP.getChipId(), ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), ESP.getBootVersion(), ESP.getBootMode(), + ESP.getCpuFreqMHz(), ESP.getFlashChipId(), reset_reason.c_str(), ESP.getResetInfo().c_str()); device_info += "|Chip: 0x" + format_hex(ESP.getChipId()); device_info += "|SDK: "; diff --git a/esphome/components/debug/debug_libretiny.cpp b/esphome/components/debug/debug_libretiny.cpp index b5e2a5b310..e823ac6c77 100644 --- a/esphome/components/debug/debug_libretiny.cpp +++ b/esphome/components/debug/debug_libretiny.cpp @@ -13,12 +13,15 @@ uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); } void DebugComponent::get_device_info_(std::string &device_info) { std::string reset_reason = get_reset_reason_(); - ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version()); - ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz()); - ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id()); - ESP_LOGD(TAG, "Board: %s", lt_get_board_code()); - ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024); - ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); + ESP_LOGD(TAG, + "LibreTiny Version: %s\n" + "Chip: %s (%04x) @ %u MHz\n" + "Chip ID: 0x%06X\n" + "Board: %s\n" + "Flash: %u KiB / RAM: %u KiB\n" + "Reset Reason: %s", + lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), lt_cpu_get_mac_id(), + lt_get_board_code(), lt_flash_get_size() / 1024, lt_ram_get_size() / 1024, reset_reason.c_str()); device_info += "|Version: "; device_info += LT_BANNER_STR + 10; diff --git a/esphome/components/debug/debug_zephyr.cpp b/esphome/components/debug/debug_zephyr.cpp index c888c41a78..6abf983e9e 100644 --- a/esphome/components/debug/debug_zephyr.cpp +++ b/esphome/components/debug/debug_zephyr.cpp @@ -106,13 +106,13 @@ static void fa_cb(const struct flash_area *fa, void *user_data) { void DebugComponent::log_partition_info_() { #if CONFIG_FLASH_MAP_LABELS ESP_LOGCONFIG(TAG, "ID | Device | Device Name " - "| Label | Offset | Size"); - ESP_LOGCONFIG(TAG, "--------------------------------------------" + "| Label | Offset | Size\n" + "--------------------------------------------" "-----------------------------------------------"); #else ESP_LOGCONFIG(TAG, "ID | Device | Device Name " - "| Offset | Size"); - ESP_LOGCONFIG(TAG, "-----------------------------------------" + "| Offset | Size\n" + "-----------------------------------------" "------------------------------"); #endif flash_area_foreach(fa_cb, nullptr); @@ -300,18 +300,18 @@ void DebugComponent::get_device_info_(std::string &device_info) { return "Unspecified"; }; - ESP_LOGD(TAG, "Code page size: %u, code size: %u, device id: 0x%08x%08x", NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, - NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0]); - ESP_LOGD(TAG, "Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x", NRF_FICR->ER[0], + ESP_LOGD(TAG, + "Code page size: %u, code size: %u, device id: 0x%08x%08x\n" + "Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n" + "Device address type: %s, address: %s\n" + "Part code: nRF%x, version: %c%c%c%c, package: %s\n" + "RAM: %ukB, Flash: %ukB, production test: %sdone", + NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0], NRF_FICR->ER[0], NRF_FICR->ER[1], NRF_FICR->ER[2], NRF_FICR->ER[3], NRF_FICR->IR[0], NRF_FICR->IR[1], NRF_FICR->IR[2], - NRF_FICR->IR[3]); - ESP_LOGD(TAG, "Device address type: %s, address: %s", (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), - get_mac_address_pretty().c_str()); - ESP_LOGD(TAG, "Part code: nRF%x, version: %c%c%c%c, package: %s", NRF_FICR->INFO.PART, - NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF, NRF_FICR->INFO.VARIANT >> 8 & 0xFF, - NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE)); - ESP_LOGD(TAG, "RAM: %ukB, Flash: %ukB, production test: %sdone", NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH, - (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not ")); + NRF_FICR->IR[3], (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), get_mac_address_pretty().c_str(), + NRF_FICR->INFO.PART, NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF, + NRF_FICR->INFO.VARIANT >> 8 & 0xFF, NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE), + NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH, (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not ")); bool n_reset_enabled = NRF_UICR->PSELRESET[0] == NRF_UICR->PSELRESET[1] && (NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos; @@ -329,9 +329,10 @@ void DebugComponent::get_device_info_(std::string &device_info) { #else ESP_LOGD(TAG, "bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF, (BOOTLOADER_VERSION_REGISTER >> 8) & 0xFF, BOOTLOADER_VERSION_REGISTER & 0xFF); - ESP_LOGD(TAG, "MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x", read_mem_u32(MBR_BOOTLOADER_ADDR), - NRF_UICR->NRFFW[0]); - ESP_LOGD(TAG, "MBR param page addr 0x%08x, UICR param page addr 0x%08x", read_mem_u32(MBR_PARAM_PAGE_ADDR), + ESP_LOGD(TAG, + "MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x\n" + "MBR param page addr 0x%08x, UICR param page addr 0x%08x", + read_mem_u32(MBR_BOOTLOADER_ADDR), NRF_UICR->NRFFW[0], read_mem_u32(MBR_PARAM_PAGE_ADDR), NRF_UICR->NRFFW[1]); if (is_sd_present()) { uint32_t const sd_id = sd_id_get(); @@ -368,8 +369,10 @@ void DebugComponent::get_device_info_(std::string &device_info) { } return res; }; - ESP_LOGD(TAG, "NRFFW %s", uicr(NRF_UICR->NRFFW, 13).c_str()); - ESP_LOGD(TAG, "NRFHW %s", uicr(NRF_UICR->NRFHW, 12).c_str()); + ESP_LOGD(TAG, + "NRFFW %s\n" + "NRFHW %s", + uicr(NRF_UICR->NRFFW, 13).c_str(), uicr(NRF_UICR->NRFHW, 12).c_str()); } void DebugComponent::update_platform_() {} diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index e0abb7c5f0..6cb204c8de 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -17,11 +17,14 @@ void DHT::setup() { } void DHT::dump_config() { - ESP_LOGCONFIG(TAG, "DHT:"); + ESP_LOGCONFIG(TAG, + "DHT:\n" + " %sModel: %s\n" + " Internal pull-up: %s", + this->is_auto_detect_ ? "Auto-detected " : "", + this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent", + ONOFF(this->t_pin_->get_flags() & gpio::FLAG_PULLUP)); LOG_PIN(" Pin: ", this->t_pin_); - ESP_LOGCONFIG(TAG, " %sModel: %s", this->is_auto_detect_ ? "Auto-detected " : "", - this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent"); - ESP_LOGCONFIG(TAG, " Internal pull-up: %s", ONOFF(this->t_pin_->get_flags() & gpio::FLAG_PULLUP)); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/endstop/endstop_cover.cpp b/esphome/components/endstop/endstop_cover.cpp index 381f098eb5..2c281ea2e6 100644 --- a/esphome/components/endstop/endstop_cover.cpp +++ b/esphome/components/endstop/endstop_cover.cpp @@ -104,10 +104,12 @@ void EndstopCover::loop() { } void EndstopCover::dump_config() { LOG_COVER("", "Endstop Cover", this); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); LOG_BINARY_SENSOR(" ", "Open Endstop", this->open_endstop_); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); LOG_BINARY_SENSOR(" ", "Close Endstop", this->close_endstop_); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); } float EndstopCover::get_setup_priority() const { return setup_priority::DATA; } void EndstopCover::stop_prev_trigger_() { diff --git a/esphome/components/epaper_spi/epaper_spi.cpp b/esphome/components/epaper_spi/epaper_spi.cpp index 4e6b4a7fd6..0b600feeae 100644 --- a/esphome/components/epaper_spi/epaper_spi.cpp +++ b/esphome/components/epaper_spi/epaper_spi.cpp @@ -331,20 +331,21 @@ void HOT EPaperBase::draw_pixel_at(int x, int y, Color color) { void EPaperBase::dump_config() { LOG_DISPLAY("", "E-Paper SPI", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->name_); - LOG_PIN(" Reset Pin: ", this->reset_pin_); - LOG_PIN(" DC Pin: ", this->dc_pin_); - LOG_PIN(" Busy Pin: ", this->busy_pin_); - LOG_PIN(" CS Pin: ", this->cs_); - LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, + " Model: %s\n" " SPI Data Rate: %uMHz\n" " Full update every: %d\n" " Swap X/Y: %s\n" " Mirror X: %s\n" " Mirror Y: %s", - (unsigned) (this->data_rate_ / 1000000), this->full_update_every_, YESNO(this->transform_ & SWAP_XY), - YESNO(this->transform_ & MIRROR_X), YESNO(this->transform_ & MIRROR_Y)); + this->name_, (unsigned) (this->data_rate_ / 1000000), this->full_update_every_, + YESNO(this->transform_ & SWAP_XY), YESNO(this->transform_ & MIRROR_X), + YESNO(this->transform_ & MIRROR_Y)); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_PIN(" CS Pin: ", this->cs_); + LOG_UPDATE_INTERVAL(this); } } // namespace esphome::epaper_spi diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 8017b577f4..26eb5dd092 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -70,9 +70,9 @@ float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_B void BLEClientBase::dump_config() { ESP_LOGCONFIG(TAG, " Address: %s\n" - " Auto-Connect: %s", - this->address_str(), TRUEFALSE(this->auto_connect_)); - ESP_LOGCONFIG(TAG, " State: %s", espbt::client_state_to_string(this->state())); + " Auto-Connect: %s\n" + " State: %s", + this->address_str(), TRUEFALSE(this->auto_connect_), espbt::client_state_to_string(this->state())); if (this->status_ == ESP_GATT_NO_RESOURCES) { ESP_LOGE(TAG, " Failed due to no resources. Try to reduce number of BLE clients in config."); } else if (this->status_ != ESP_GATT_OK) { @@ -415,8 +415,10 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ for (auto &svc : this->services_) { char uuid_buf[espbt::UUID_STR_LEN]; svc->uuid.to_str(uuid_buf); - ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_, uuid_buf); - ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, this->address_str_, + ESP_LOGV(TAG, + "[%d] [%s] Service UUID: %s\n" + "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", + this->connection_index_, this->address_str_, uuid_buf, this->connection_index_, this->address_str_, svc->start_handle, svc->end_handle); } #endif diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index 0aaef597d3..cc6bfbc8a8 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -18,9 +18,11 @@ void ESP8266PWM::setup() { this->turn_off(); } void ESP8266PWM::dump_config() { - ESP_LOGCONFIG(TAG, "ESP8266 PWM:"); + ESP_LOGCONFIG(TAG, + "ESP8266 PWM:\n" + " Frequency: %.1f Hz", + this->frequency_); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); LOG_FLOAT_OUTPUT(this); } void HOT ESP8266PWM::write_state(float state) { diff --git a/esphome/components/esp_ldo/esp_ldo.cpp b/esphome/components/esp_ldo/esp_ldo.cpp index 5e3d4159f3..2eee855b46 100644 --- a/esphome/components/esp_ldo/esp_ldo.cpp +++ b/esphome/components/esp_ldo/esp_ldo.cpp @@ -21,9 +21,11 @@ void EspLdo::setup() { } } void EspLdo::dump_config() { - ESP_LOGCONFIG(TAG, "ESP LDO Channel %d:", this->channel_); - ESP_LOGCONFIG(TAG, " Voltage: %fV", this->voltage_); - ESP_LOGCONFIG(TAG, " Adjustable: %s", YESNO(this->adjustable_)); + ESP_LOGCONFIG(TAG, + "ESP LDO Channel %d:\n" + " Voltage: %fV\n" + " Adjustable: %s", + this->channel_, this->voltage_, YESNO(this->adjustable_)); } void EspLdo::adjust_voltage(float voltage) { diff --git a/esphome/components/espnow/packet_transport/espnow_transport.cpp b/esphome/components/espnow/packet_transport/espnow_transport.cpp index c1252acc9d..3d16f28c7d 100644 --- a/esphome/components/espnow/packet_transport/espnow_transport.cpp +++ b/esphome/components/espnow/packet_transport/espnow_transport.cpp @@ -21,9 +21,11 @@ void ESPNowTransport::setup() { return; } - ESP_LOGI(TAG, "Registering ESP-NOW handlers"); - ESP_LOGI(TAG, "Peer address: %02X:%02X:%02X:%02X:%02X:%02X", this->peer_address_[0], this->peer_address_[1], - this->peer_address_[2], this->peer_address_[3], this->peer_address_[4], this->peer_address_[5]); + ESP_LOGI(TAG, + "Registering ESP-NOW handlers\n" + "Peer address: %02X:%02X:%02X:%02X:%02X:%02X", + this->peer_address_[0], this->peer_address_[1], this->peer_address_[2], this->peer_address_[3], + this->peer_address_[4], this->peer_address_[5]); // Register received handler this->parent_->register_received_handler(this); diff --git a/esphome/components/ezo_pmp/ezo_pmp.cpp b/esphome/components/ezo_pmp/ezo_pmp.cpp index 9ec41cce30..61b601328a 100644 --- a/esphome/components/ezo_pmp/ezo_pmp.cpp +++ b/esphome/components/ezo_pmp/ezo_pmp.cpp @@ -148,10 +148,13 @@ void EzoPMP::read_command_result_() { char current_char = response_buffer[i]; if (current_char == '\0') { - ESP_LOGV(TAG, "Read Response from device: %s", (char *) response_buffer); - ESP_LOGV(TAG, "First Component: %s", (char *) first_parameter_buffer); - ESP_LOGV(TAG, "Second Component: %s", (char *) second_parameter_buffer); - ESP_LOGV(TAG, "Third Component: %s", (char *) third_parameter_buffer); + ESP_LOGV(TAG, + "Read Response from device: %s\n" + "First Component: %s\n" + "Second Component: %s\n" + "Third Component: %s", + (char *) response_buffer, (char *) first_parameter_buffer, (char *) second_parameter_buffer, + (char *) third_parameter_buffer); break; } diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 4eacd913fa..cd035fc0e9 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -179,8 +179,10 @@ void Fan::add_on_state_callback(std::function &&callback) { this->state_ void Fan::publish_state() { auto traits = this->get_traits(); - ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str()); - ESP_LOGD(TAG, " State: %s", ONOFF(this->state)); + ESP_LOGD(TAG, + "'%s' - Sending state:\n" + " State: %s", + this->name_.c_str(), ONOFF(this->state)); if (traits.supports_speed()) { ESP_LOGD(TAG, " Speed: %d", this->speed); } diff --git a/esphome/components/template/water_heater/template_water_heater.cpp b/esphome/components/template/water_heater/template_water_heater.cpp index 18ef8d3f06..5ae5c30f36 100644 --- a/esphome/components/template/water_heater/template_water_heater.cpp +++ b/esphome/components/template/water_heater/template_water_heater.cpp @@ -21,7 +21,7 @@ void TemplateWaterHeater::setup() { } water_heater::WaterHeaterTraits TemplateWaterHeater::traits() { - auto traits = water_heater::WaterHeater::get_traits(); + water_heater::WaterHeaterTraits traits; if (!this->supported_modes_.empty()) { traits.set_supported_modes(this->supported_modes_); diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 3677717b8c..bd32ba33bf 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -431,11 +431,8 @@ void VoiceAssistant::client_subscription(api::APIConnection *client, bool subscr if (this->api_client_ != nullptr) { ESP_LOGE(TAG, "Multiple API Clients attempting to connect to Voice Assistant"); - char peername[socket::PEERNAME_MAX_LEN]; - this->api_client_->get_peername_to(peername); - ESP_LOGE(TAG, "Current client: %s (%s)", this->api_client_->get_name(), peername); - client->get_peername_to(peername); - ESP_LOGE(TAG, "New client: %s (%s)", client->get_name(), peername); + ESP_LOGE(TAG, "Current client: %s (%s)", this->api_client_->get_name(), this->api_client_->get_peername()); + ESP_LOGE(TAG, "New client: %s (%s)", client->get_name(), client->get_peername()); return; } diff --git a/tests/components/water_heater/common.yaml b/tests/components/water_heater/common.yaml new file mode 100644 index 0000000000..8ec2b1b297 --- /dev/null +++ b/tests/components/water_heater/common.yaml @@ -0,0 +1,16 @@ +water_heater: + - platform: template + id: my_boiler + name: "Test Boiler" + min_temperature: 10 + max_temperature: 85 + target_temperature_step: 0.5 + current_temperature_step: 0.1 + optimistic: true + current_temperature: 45.0 + mode: !lambda "return water_heater::WATER_HEATER_MODE_ECO;" + visual: + min_temperature: 10 + max_temperature: 85 + target_temperature_step: 0.5 + current_temperature_step: 0.1 diff --git a/tests/components/web_server/common.yaml b/tests/components/web_server/common.yaml index eb768eeb91..82307c189c 100644 --- a/tests/components/web_server/common.yaml +++ b/tests/components/web_server/common.yaml @@ -36,3 +36,4 @@ datetime: optimistic: yes event: update: +water_heater: diff --git a/tests/integration/fixtures/water_heater_template.yaml b/tests/integration/fixtures/water_heater_template.yaml new file mode 100644 index 0000000000..b54ebed789 --- /dev/null +++ b/tests/integration/fixtures/water_heater_template.yaml @@ -0,0 +1,23 @@ +esphome: + name: water-heater-template-test +host: +api: +logger: + +water_heater: + - platform: template + id: test_boiler + name: Test Boiler + optimistic: true + current_temperature: !lambda "return 45.0f;" + # Note: No mode lambda - we want optimistic mode changes to stick + # A mode lambda would override mode changes in loop() + supported_modes: + - "off" + - eco + - gas + - performance + visual: + min_temperature: 30.0 + max_temperature: 85.0 + target_temperature_step: 0.5 diff --git a/tests/integration/test_water_heater_template.py b/tests/integration/test_water_heater_template.py new file mode 100644 index 0000000000..b5f1fb64c0 --- /dev/null +++ b/tests/integration/test_water_heater_template.py @@ -0,0 +1,109 @@ +"""Integration test for template water heater component.""" + +from __future__ import annotations + +import asyncio + +import aioesphomeapi +from aioesphomeapi import WaterHeaterInfo, WaterHeaterMode, WaterHeaterState +import pytest + +from .state_utils import InitialStateHelper +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_water_heater_template( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test template water heater basic state and mode changes.""" + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + states: dict[int, aioesphomeapi.EntityState] = {} + gas_mode_future: asyncio.Future[WaterHeaterState] = loop.create_future() + eco_mode_future: asyncio.Future[WaterHeaterState] = loop.create_future() + + def on_state(state: aioesphomeapi.EntityState) -> None: + states[state.key] = state + if isinstance(state, WaterHeaterState): + # Wait for GAS mode + if state.mode == WaterHeaterMode.GAS and not gas_mode_future.done(): + gas_mode_future.set_result(state) + # Wait for ECO mode (we start at OFF, so test transitioning to ECO) + elif state.mode == WaterHeaterMode.ECO and not eco_mode_future.done(): + eco_mode_future.set_result(state) + + # Get entities and set up state synchronization + entities, services = await client.list_entities_services() + initial_state_helper = InitialStateHelper(entities) + water_heater_infos = [e for e in entities if isinstance(e, WaterHeaterInfo)] + assert len(water_heater_infos) == 1, ( + f"Expected exactly 1 water heater entity, got {len(water_heater_infos)}. Entity types: {[type(e).__name__ for e in entities]}" + ) + + test_water_heater = water_heater_infos[0] + + # Verify water heater entity info + assert test_water_heater.object_id == "test_boiler" + assert test_water_heater.name == "Test Boiler" + assert test_water_heater.min_temperature == 30.0 + assert test_water_heater.max_temperature == 85.0 + assert test_water_heater.target_temperature_step == 0.5 + + # Verify supported modes + supported_modes = test_water_heater.supported_modes + assert WaterHeaterMode.OFF in supported_modes, "Expected OFF in supported modes" + assert WaterHeaterMode.ECO in supported_modes, "Expected ECO in supported modes" + assert WaterHeaterMode.GAS in supported_modes, "Expected GAS in supported modes" + assert WaterHeaterMode.PERFORMANCE in supported_modes, ( + "Expected PERFORMANCE in supported modes" + ) + assert len(supported_modes) == 4, ( + f"Expected 4 supported modes, got {len(supported_modes)}: {supported_modes}" + ) + + # Subscribe with the wrapper that filters initial states + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + + # Wait for all initial states to be broadcast + try: + await initial_state_helper.wait_for_initial_states() + except TimeoutError: + pytest.fail("Timeout waiting for initial states") + + # Get initial state and verify + initial_state = initial_state_helper.initial_states.get(test_water_heater.key) + assert initial_state is not None, "Water heater initial state not found" + assert isinstance(initial_state, WaterHeaterState) + # Initial mode is OFF (default) since we don't have a mode lambda + # A mode lambda would override optimistic mode changes + assert initial_state.mode == WaterHeaterMode.OFF, ( + f"Expected initial mode OFF, got {initial_state.mode}" + ) + assert initial_state.current_temperature == 45.0, ( + f"Expected current temp 45.0, got {initial_state.current_temperature}" + ) + + # Test changing to GAS mode + client.water_heater_command(test_water_heater.key, mode=WaterHeaterMode.GAS) + + try: + gas_state = await asyncio.wait_for(gas_mode_future, timeout=5.0) + except TimeoutError: + pytest.fail("GAS mode change not received within 5 seconds") + + assert isinstance(gas_state, WaterHeaterState) + assert gas_state.mode == WaterHeaterMode.GAS + + # Test changing to ECO mode (from GAS) + client.water_heater_command(test_water_heater.key, mode=WaterHeaterMode.ECO) + + try: + eco_state = await asyncio.wait_for(eco_mode_future, timeout=5.0) + except TimeoutError: + pytest.fail("ECO mode change not received within 5 seconds") + + assert isinstance(eco_state, WaterHeaterState) + assert eco_state.mode == WaterHeaterMode.ECO