Replace the four explicit nvs_flash_init() calls with a small RAII
guard (NvsReinitGuard) declared right after nvs_flash_deinit(). Each
failure path now just returns; the guard reinits NVS in its destructor.
The success path disarms it before the trailing return because the
device reboots immediately afterwards and reinit would only churn the
partition cache.
No behaviour change.
- upload_program: allow MQTT/MQTTIP devices for --partition-table.
MQTTIP gets resolved to a real IP by _resolve_network_devices(), so
rejecting any non-NETWORK port_type was incorrect; only SERIAL and
BOOTSEL are non-OTA upload paths.
- update_partition_table: re-initialize NVS on every failure path past
nvs_flash_deinit() so components that survive a failed OTA aren't
left with broken NVS handles. Success path stays as-is because the
device reboots immediately afterwards.
Adds an MQTTIP upload test and refreshes the gate's comment.
The running-app position cache lived as three function-local statics
inside get_running_app_position(). They cannot be IDFOTABackend members
(the backend is per-connection, the cache must outlive a backend that
called esp_partition_unload_all() in a prior aborted partition-table
OTA), but burying them inside the function made the lifetime and
shared-across-connections semantics implicit. Move them to file scope
with s_running_app_ prefix so the process-scoped lifetime is visible
at first read, and tighten the surrounding comments.
No behaviour change.
Splits the non-destructive validation phase out of update_partition_table()
into a dedicated method. update_partition_table() now reads top-down as
"check buffer state, find running app, validate the new table + plan the
target slot, then commit", with the destructive write isolated to the
final block.
The chosen slot and optional copy-source partition are returned via a
small PartitionTablePlan struct so the caller no longer juggles the two
candidate-index variables. Refactor only; behaviour and error semantics
are unchanged.
Split the combined "missing app, otadata, or nvs" verify failure into
three separate ESP_LOGE messages so users can see which check failed,
trim trailing spaces from the (err=0x%X) log strings, and document why
the partition-table espota2 test mocks SERVER_FEATURE_SUPPORTS_COMPRESSION
(intentional protocol-path coverage; the real IDFOTABackend never sets it).
Read the resolved partition-table file in upload_program before opening
a network connection. Reject anything that isn't 0xC00 bytes, doesn't
start with ESP_PARTITION_MAGIC, or is missing the MD5 checksum entry, so
mistakes (wrong file, swapped --file path) surface as a local error
instead of a post-handshake OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY.
Includes unit tests covering size, magic, md5-presence, missing-file,
and end-to-end upload_program rejection, plus three real partition
tables checked in as fixtures (ESPHome build, ESP-IDF Hello-world,
esphome_dashboard prebuilt).