From ddbae0dd005dfc67136fd9126143bd68d7e70a74 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Mar 2026 13:08:12 -1000 Subject: [PATCH] [rp2040] Always show BOOTSEL tip when no BOOTSEL device detected Serial ports in the chooser may belong to other devices (e.g. FT232R), not the RP2040. Show the BOOTSEL tip whenever no BOOTSEL device is detected via picotool, regardless of whether serial ports are present. --- esphome/__main__.py | 7 ++----- tests/unit_tests/test_main.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 72f0ff34e2..d0a1811bd4 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -281,14 +281,11 @@ def choose_upload_log_host( if has_mqtt_ip_lookup(): options.append(("Over The Air (MQTT IP lookup)", "MQTTIP")) - # Show helpful BOOTSEL instructions for RP2040 when no USB device is found + # Show helpful BOOTSEL instructions for RP2040 when no BOOTSEL device is found if ( purpose == Purpose.UPLOADING and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040 - and not any( - get_port_type(opt[1]) in (PortType.SERIAL, PortType.BOOTSEL) - for opt in options - ) + and not any(get_port_type(opt[1]) == PortType.BOOTSEL for opt in options) ): if not options: raise EsphomeError( diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 32ebeaacf2..31d5225914 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -934,6 +934,31 @@ def test_choose_upload_log_host_rp2040_bootsel_tip_with_ota( assert "BOOTSEL" in caplog.text +def test_choose_upload_log_host_rp2040_bootsel_tip_with_serial_ports( + caplog: pytest.LogCaptureFixture, + mock_choose_prompt: Mock, +) -> None: + """Test BOOTSEL tip shown when serial ports exist but no BOOTSEL device.""" + setup_core(platform=PLATFORM_RP2040) + + mock_ports = [MockSerialPort("/dev/ttyACM0", "RP2040 Serial")] + with ( + patch("esphome.__main__.get_serial_ports", return_value=mock_ports), + patch( + "esphome.__main__._find_picotool", + return_value=Path("/usr/bin/picotool"), + ), + patch("esphome.__main__.detect_rp2040_bootsel", return_value=0), + caplog.at_level(logging.INFO, logger="esphome.__main__"), + ): + choose_upload_log_host( + default=None, + check_default=None, + purpose=Purpose.UPLOADING, + ) + assert "BOOTSEL" in caplog.text + + def test_choose_upload_log_host_no_bootsel_for_non_rp2040( mock_no_serial_ports: Mock, ) -> None: