diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index 5843a010f3a..053887a7dba 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -208,7 +208,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { nullptr, ESP_PRIMARY_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_SIZE, "PrimaryPrtTable", ESP_PARTITION_TYPE_PARTITION_TABLE, ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY, &this->partition_table_part_); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_partition_register_external failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_partition_register_external failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; } @@ -218,13 +218,13 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { err = esp_partition_mmap(this->partition_table_part_, 0, ESP_PARTITION_TABLE_MAX_LEN, ESP_PARTITION_MMAP_DATA, reinterpret_cast(&existing_partition_table), &partition_table_map); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_partition_mmap failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_partition_mmap failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; } err = esp_partition_table_verify(existing_partition_table, true, &num_partitions); esp_partition_munmap(partition_table_map); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_partition_table_verify failed (existing partition table) (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_partition_table_verify failed (existing partition table) (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; } @@ -233,7 +233,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { // esp_partition_table_verify expects ESP_PARTITION_TABLE_MAX_LEN bytes of data err = esp_partition_table_verify(new_partition_table, true, &num_partitions); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_partition_table_verify failed (new partition table) (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_partition_table_verify failed (new partition table) (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; } // Check for missing checksum @@ -303,8 +303,16 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { ESP_LOGE(TAG, "No compatible app partition found in the new partition table"); return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; } - if (app_partitions_found < 2 || !otadata_partition_found || !nvs_partition_found) { - ESP_LOGE(TAG, "New partition table is missing the required app, otadata or nvs partitions"); + if (app_partitions_found < 2) { + ESP_LOGE(TAG, "New partition table needs at least 2 app partitions, found %d", app_partitions_found); + return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; + } + if (!otadata_partition_found) { + ESP_LOGE(TAG, "New partition table is missing the required otadata partition"); + return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; + } + if (!nvs_partition_found) { + ESP_LOGE(TAG, "New partition table is missing the required nvs partition"); return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY; } if (otadata_overlap) { @@ -337,7 +345,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { err = esp_partition_copy(app_copy_target_part, 0, running_app_part, 0, running_app_size); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_partition_copy failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_partition_copy failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; } } @@ -352,7 +360,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { if (err != ESP_OK) { esp_ota_abort(this->update_handle_); this->update_handle_ = 0; - ESP_LOGE(TAG, "esp_ota_begin failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_ota_begin failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; } err = esp_ota_write(this->update_handle_, this->buf_, ESP_PARTITION_TABLE_MAX_LEN); @@ -361,13 +369,13 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { // partial-write failure path self-contained. esp_ota_abort(this->update_handle_); this->update_handle_ = 0; - ESP_LOGE(TAG, "esp_ota_write failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_ota_write failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; } err = esp_ota_end(this->update_handle_); this->update_handle_ = 0; // esp_ota_end releases the handle internally regardless of result if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_end failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_ota_end failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; } // esp_partition_unload_all() invalidates every cached partition entry, including the externally @@ -388,7 +396,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() { ESP_LOGD(TAG, "Setting next boot partition to 0x%X", new_boot_partition->address); err = esp_ota_set_boot_partition(new_boot_partition); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (err=0x%X) ", err); + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (err=0x%X)", err); return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; } return OTA_RESPONSE_OK; diff --git a/tests/unit_tests/test_espota2.py b/tests/unit_tests/test_espota2.py index 28e30bbe77f..2cad1d2ec81 100644 --- a/tests/unit_tests/test_espota2.py +++ b/tests/unit_tests/test_espota2.py @@ -843,7 +843,14 @@ def test_perform_ota_extended_protocol_app( def test_perform_ota_successful_partition_table( mock_socket: Mock, mock_file: io.BytesIO ) -> None: - """Test OTA partition table update.""" + """Test OTA partition table update. + + The mocked server advertises both COMPRESSION and PARTITION_ACCESS to exercise + the full extended-protocol negotiation path. Real IDFOTABackend devices return + ``supports_compression() == false`` and never set the COMPRESSION flag for a + partition-table OTA; the flag here is intentional protocol-coverage, not a + description of on-device behaviour. + """ recv_responses = [ bytes([espota2.RESPONSE_OK]), # First byte of version response bytes([espota2.OTA_VERSION_2_0]), # Version number @@ -853,7 +860,7 @@ def test_perform_ota_successful_partition_table( espota2.SERVER_FEATURE_SUPPORTS_COMPRESSION | espota2.SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS ] - ), # Device feature flags + ), # Device feature flags (compression flag is unrealistic; see docstring) bytes([espota2.RESPONSE_AUTH_OK]), # No auth required bytes([espota2.RESPONSE_UPDATE_PREPARE_OK]), # Binary size OK bytes([espota2.RESPONSE_BIN_MD5_OK]), # MD5 checksum OK