Files
esphome/tests/unit_tests/components/test_nrf52.py
T
J. Nick Koston d96ad02b9f [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
2026-05-10 23:07:04 -05:00

53 lines
1.8 KiB
Python

"""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()]