Skip to content

Commit ed9cfb4

Browse files
puddlyfrenck
authored andcommitted
Use hardware bootloader reset methods for firmware config flows (home-assistant#153277)
1 parent a6b6e4c commit ed9cfb4

File tree

16 files changed

+174
-64
lines changed

16 files changed

+174
-64
lines changed

homeassistant/components/homeassistant_connect_zbt2/config_flow.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from homeassistant.components.homeassistant_hardware.util import (
1111
ApplicationType,
1212
FirmwareInfo,
13+
ResetTarget,
1314
)
1415
from homeassistant.config_entries import (
1516
ConfigEntry,
@@ -67,6 +68,11 @@ class ZBT2FirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
6768

6869
context: ConfigFlowContext
6970

71+
# `rts_dtr` targets older adapters, `baudrate` works for newer ones. The reason we
72+
# try them in this order is that on older adapters `baudrate` entered the ESP32-S3
73+
# bootloader instead of the MG24 bootloader.
74+
BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR, ResetTarget.BAUDRATE]
75+
7076
async def async_step_install_zigbee_firmware(
7177
self, user_input: dict[str, Any] | None = None
7278
) -> ConfigFlowResult:

homeassistant/components/homeassistant_connect_zbt2/update.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from homeassistant.components.homeassistant_hardware.util import (
1717
ApplicationType,
1818
FirmwareInfo,
19+
ResetTarget,
1920
)
2021
from homeassistant.components.update import UpdateDeviceClass
2122
from homeassistant.config_entries import ConfigEntry
@@ -156,7 +157,7 @@ async def async_setup_entry(
156157
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
157158
"""Connect ZBT-2 firmware update entity."""
158159

159-
bootloader_reset_type = None
160+
bootloader_reset_methods = [ResetTarget.RTS_DTR, ResetTarget.BAUDRATE]
160161

161162
def __init__(
162163
self,

homeassistant/components/homeassistant_hardware/firmware_config_flow.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
FirmwareInfo,
4040
OwningAddon,
4141
OwningIntegration,
42+
ResetTarget,
4243
async_flash_silabs_firmware,
4344
get_otbr_addon_manager,
4445
guess_firmware_info,
@@ -79,6 +80,8 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
7980
"""Base flow to install firmware."""
8081

8182
ZIGBEE_BAUDRATE = 115200 # Default, subclasses may override
83+
BOOTLOADER_RESET_METHODS: list[ResetTarget] = [] # Default, subclasses may override
84+
8285
_picked_firmware_type: PickedFirmwareType
8386
_zigbee_flow_strategy: ZigbeeFlowStrategy = ZigbeeFlowStrategy.RECOMMENDED
8487

@@ -274,7 +277,7 @@ async def _install_firmware(
274277
device=self._device,
275278
fw_data=fw_data,
276279
expected_installed_firmware_type=expected_installed_firmware_type,
277-
bootloader_reset_type=None,
280+
bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS,
278281
progress_callback=lambda offset, total: self.async_update_progress(
279282
offset / total
280283
),

homeassistant/components/homeassistant_hardware/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware",
77
"integration_type": "system",
88
"requirements": [
9-
"universal-silabs-flasher==0.0.32",
9+
"universal-silabs-flasher==0.0.34",
1010
"ha-silabs-firmware-client==0.2.0"
1111
]
1212
}

homeassistant/components/homeassistant_hardware/update.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@
2222

2323
from .coordinator import FirmwareUpdateCoordinator
2424
from .helpers import async_register_firmware_info_callback
25-
from .util import ApplicationType, FirmwareInfo, async_flash_silabs_firmware
25+
from .util import (
26+
ApplicationType,
27+
FirmwareInfo,
28+
ResetTarget,
29+
async_flash_silabs_firmware,
30+
)
2631

2732
_LOGGER = logging.getLogger(__name__)
2833

@@ -81,7 +86,7 @@ class BaseFirmwareUpdateEntity(
8186

8287
# Subclasses provide the mapping between firmware types and entity descriptions
8388
entity_description: FirmwareUpdateEntityDescription
84-
bootloader_reset_type: str | None = None
89+
bootloader_reset_methods: list[ResetTarget] = []
8590

8691
_attr_supported_features = (
8792
UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
@@ -268,7 +273,7 @@ async def async_install(
268273
device=self._current_device,
269274
fw_data=fw_data,
270275
expected_installed_firmware_type=self.entity_description.expected_firmware_type,
271-
bootloader_reset_type=self.bootloader_reset_type,
276+
bootloader_reset_methods=self.bootloader_reset_methods,
272277
progress_callback=self._update_progress,
273278
)
274279
finally:

homeassistant/components/homeassistant_hardware/util.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
import asyncio
66
from collections import defaultdict
7-
from collections.abc import AsyncIterator, Callable, Iterable
7+
from collections.abc import AsyncIterator, Callable, Iterable, Sequence
88
from contextlib import AsyncExitStack, asynccontextmanager
99
from dataclasses import dataclass
1010
from enum import StrEnum
1111
import logging
1212

13-
from universal_silabs_flasher.const import ApplicationType as FlasherApplicationType
13+
from universal_silabs_flasher.const import (
14+
ApplicationType as FlasherApplicationType,
15+
ResetTarget as FlasherResetTarget,
16+
)
1417
from universal_silabs_flasher.firmware import parse_firmware_image
1518
from universal_silabs_flasher.flasher import Flasher
1619

@@ -59,6 +62,18 @@ def as_flasher_application_type(self) -> FlasherApplicationType:
5962
return FlasherApplicationType(self.value)
6063

6164

65+
class ResetTarget(StrEnum):
66+
"""Methods to reset a device into bootloader mode."""
67+
68+
RTS_DTR = "rts_dtr"
69+
BAUDRATE = "baudrate"
70+
YELLOW = "yellow"
71+
72+
def as_flasher_reset_target(self) -> FlasherResetTarget:
73+
"""Convert the reset target enum into one compatible with USF."""
74+
return FlasherResetTarget(self.value)
75+
76+
6277
@singleton(OTBR_ADDON_MANAGER_DATA)
6378
@callback
6479
def get_otbr_addon_manager(hass: HomeAssistant) -> WaitingAddonManager:
@@ -342,7 +357,7 @@ async def async_flash_silabs_firmware(
342357
device: str,
343358
fw_data: bytes,
344359
expected_installed_firmware_type: ApplicationType,
345-
bootloader_reset_type: str | None = None,
360+
bootloader_reset_methods: Sequence[ResetTarget] = (),
346361
progress_callback: Callable[[int, int], None] | None = None,
347362
) -> FirmwareInfo:
348363
"""Flash firmware to the SiLabs device."""
@@ -359,7 +374,9 @@ async def async_flash_silabs_firmware(
359374
ApplicationType.SPINEL.as_flasher_application_type(),
360375
ApplicationType.CPC.as_flasher_application_type(),
361376
),
362-
bootloader_reset=bootloader_reset_type,
377+
bootloader_reset=tuple(
378+
m.as_flasher_reset_target() for m in bootloader_reset_methods
379+
),
363380
)
364381

365382
async with AsyncExitStack() as stack:

homeassistant/components/homeassistant_sky_connect/update.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ async def async_setup_entry(
168168
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
169169
"""SkyConnect firmware update entity."""
170170

171-
bootloader_reset_type = None
171+
# The ZBT-1 does not have a hardware bootloader trigger
172+
bootloader_reset_methods = []
172173

173174
def __init__(
174175
self,

homeassistant/components/homeassistant_yellow/config_flow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from homeassistant.components.homeassistant_hardware.util import (
2828
ApplicationType,
2929
FirmwareInfo,
30+
ResetTarget,
3031
probe_silabs_firmware_info,
3132
)
3233
from homeassistant.config_entries import (
@@ -83,6 +84,8 @@ async def _install_firmware_step(
8384
class YellowFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
8485
"""Mixin for Home Assistant Yellow firmware methods."""
8586

87+
BOOTLOADER_RESET_METHODS = [ResetTarget.YELLOW]
88+
8689
async def async_step_install_zigbee_firmware(
8790
self, user_input: dict[str, Any] | None = None
8891
) -> ConfigFlowResult:

homeassistant/components/homeassistant_yellow/update.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from homeassistant.components.homeassistant_hardware.util import (
1717
ApplicationType,
1818
FirmwareInfo,
19+
ResetTarget,
1920
)
2021
from homeassistant.components.update import UpdateDeviceClass
2122
from homeassistant.config_entries import ConfigEntry
@@ -173,7 +174,7 @@ async def async_setup_entry(
173174
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
174175
"""Yellow firmware update entity."""
175176

176-
bootloader_reset_type = "yellow" # Triggers a GPIO reset
177+
bootloader_reset_methods = [ResetTarget.YELLOW] # Triggers a GPIO reset
177178

178179
def __init__(
179180
self,

requirements_all.txt

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)