mirror of
https://github.com/esphome/esphome.git
synced 2026-09-19 11:08:38 +00:00
[nrf52] Honor --prebuilt-dir for mcumgr/BLE OTA uploads
nRF52 has its own upload_program that runs before the default dispatch
in esphome.__main__. The mcumgr/BLE OTA path reads the MCUboot-signed
update image from CORE.relative_pioenvs_path(name, 'zephyr',
'app_update.bin'), which assumes a local Zephyr build tree.
Consult CORE.prebuilt_artifact_path('app_update.bin') first so the
dashboard's transparent BLE install on a Bluetooth proxy can flash a
prebuilt update image without compiling locally.
Serial uploads on non-MCUboot bootloaders still go through
adafruit-nrfutil via _upload_using_platformio and are out of scope here;
they need their own bypass to work with --prebuilt-dir (tracked
separately).
Issue: esphome/device-builder#572
This commit is contained in:
@@ -436,9 +436,17 @@ def upload_program(config: ConfigType, args, host: str) -> bool:
|
||||
mcumgr_device = host
|
||||
|
||||
if mcumgr_device:
|
||||
firmware = Path(
|
||||
CORE.relative_pioenvs_path(CORE.name, "zephyr", "app_update.bin")
|
||||
).resolve()
|
||||
# `esphome upload --prebuilt-dir <path>` ships the MCUboot-signed
|
||||
# update image at the root of the prebuilt directory; prefer that so
|
||||
# the dashboard's transparent BLE install on a Bluetooth proxy works
|
||||
# without a local Zephyr build tree.
|
||||
prebuilt = CORE.prebuilt_artifact_path("app_update.bin")
|
||||
if prebuilt is not None:
|
||||
firmware = prebuilt.resolve()
|
||||
else:
|
||||
firmware = Path(
|
||||
CORE.relative_pioenvs_path(CORE.name, "zephyr", "app_update.bin")
|
||||
).resolve()
|
||||
asyncio.run(smpmgr_upload(mcumgr_device, firmware))
|
||||
return True # Handled: mcumgr OTA upload
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Tests for the nrf52 component's custom upload_program."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
def test_nrf52_upload_program_prebuilt_dir_for_ble_ota(tmp_path: Path) -> None:
|
||||
"""`esphome upload --prebuilt-dir <path>` for an nRF52 Bluetooth proxy
|
||||
ships the MCUboot-signed update at <prebuilt-dir>/app_update.bin so the
|
||||
dashboard's transparent BLE install doesn't need a local Zephyr build
|
||||
tree. Verify the nrf52 upload_program picks up that path instead of
|
||||
`CORE.relative_pioenvs_path(..., "zephyr", "app_update.bin")`.
|
||||
"""
|
||||
prebuilt = tmp_path / "prebuilt"
|
||||
prebuilt.mkdir()
|
||||
expected_firmware = prebuilt / "app_update.bin"
|
||||
expected_firmware.write_bytes(b"signed-mcuboot-image")
|
||||
CORE.prebuilt_dir = prebuilt
|
||||
CORE.name = "bt-proxy"
|
||||
|
||||
captured_firmware: list[Path] = []
|
||||
|
||||
async def fake_upload(device: str, firmware: Path) -> None:
|
||||
captured_firmware.append(firmware)
|
||||
|
||||
# Patch the heavy nrf52 surface so we exercise only the firmware-path
|
||||
# resolution: BLE scan returns a fake device, smpmgr_upload records the
|
||||
# firmware path it would have flashed.
|
||||
with (
|
||||
patch(
|
||||
"esphome.components.nrf52.ota.smpmgr_scan",
|
||||
new=AsyncMock(return_value="AA:BB:CC:DD:EE:FF"),
|
||||
),
|
||||
patch(
|
||||
"esphome.components.nrf52.ota.smpmgr_upload",
|
||||
new=fake_upload,
|
||||
),
|
||||
patch(
|
||||
"esphome.components.nrf52.ble_logger.is_mac_address",
|
||||
return_value=False,
|
||||
),
|
||||
):
|
||||
from esphome.components.nrf52 import upload_program
|
||||
|
||||
handled = upload_program({}, MagicMock(), "BLE")
|
||||
|
||||
assert handled is True
|
||||
assert captured_firmware == [expected_firmware.resolve()]
|
||||
Reference in New Issue
Block a user