Skip to content

Commit 7073c40

Browse files
puddlyTheJulianJES
authored andcommitted
Bump universal-silabs-flasher to v0.1.0 (home-assistant#156291)
Co-authored-by: TheJulianJES <[email protected]>
1 parent 8fb9d92 commit 7073c40

File tree

17 files changed

+189
-38
lines changed

17 files changed

+189
-38
lines changed

homeassistant/components/homeassistant_connect_zbt2/config_flow.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,18 @@ class ZBT2FirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
7676
"""Mixin for Home Assistant Connect ZBT-2 firmware methods."""
7777

7878
context: ConfigFlowContext
79-
BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR]
79+
8080
ZIGBEE_BAUDRATE = 460800
8181

82+
# Early ZBT-2 samples used RTS/DTR to trigger the bootloader, later ones use the
83+
# baudrate method. Since the two are mutually exclusive we just use both.
84+
BOOTLOADER_RESET_METHODS = [ResetTarget.RTS_DTR, ResetTarget.BAUDRATE]
85+
APPLICATION_PROBE_METHODS = [
86+
(ApplicationType.GECKO_BOOTLOADER, 115200),
87+
(ApplicationType.EZSP, ZIGBEE_BAUDRATE),
88+
(ApplicationType.SPINEL, 460800),
89+
]
90+
8291
async def async_step_install_zigbee_firmware(
8392
self, user_input: dict[str, Any] | None = None
8493
) -> ConfigFlowResult:

homeassistant/components/homeassistant_connect_zbt2/update.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from homeassistant.components.homeassistant_hardware.util import (
1515
ApplicationType,
1616
FirmwareInfo,
17-
ResetTarget,
1817
)
1918
from homeassistant.components.update import UpdateDeviceClass
2019
from homeassistant.const import EntityCategory
@@ -24,6 +23,7 @@
2423
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
2524

2625
from . import HomeAssistantConnectZBT2ConfigEntry
26+
from .config_flow import ZBT2FirmwareMixin
2727
from .const import DOMAIN, FIRMWARE, FIRMWARE_VERSION, HARDWARE_NAME, SERIAL_NUMBER
2828

2929
_LOGGER = logging.getLogger(__name__)
@@ -134,7 +134,8 @@ async def async_setup_entry(
134134
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
135135
"""Connect ZBT-2 firmware update entity."""
136136

137-
bootloader_reset_methods = [ResetTarget.RTS_DTR]
137+
BOOTLOADER_RESET_METHODS = ZBT2FirmwareMixin.BOOTLOADER_RESET_METHODS
138+
APPLICATION_PROBE_METHODS = ZBT2FirmwareMixin.APPLICATION_PROBE_METHODS
138139

139140
def __init__(
140141
self,

homeassistant/components/homeassistant_hardware/firmware_config_flow.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class BaseFirmwareInstallFlow(ConfigEntryBaseFlow, ABC):
8181

8282
ZIGBEE_BAUDRATE = 115200 # Default, subclasses may override
8383
BOOTLOADER_RESET_METHODS: list[ResetTarget] = [] # Default, subclasses may override
84+
APPLICATION_PROBE_METHODS: list[tuple[ApplicationType, int]] = []
8485

8586
_picked_firmware_type: PickedFirmwareType
8687
_zigbee_flow_strategy: ZigbeeFlowStrategy = ZigbeeFlowStrategy.RECOMMENDED
@@ -230,7 +231,11 @@ async def _install_firmware(
230231
# Installing new firmware is only truly required if the wrong type is
231232
# installed: upgrading to the latest release of the current firmware type
232233
# isn't strictly necessary for functionality.
233-
self._probed_firmware_info = await probe_silabs_firmware_info(self._device)
234+
self._probed_firmware_info = await probe_silabs_firmware_info(
235+
self._device,
236+
bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS,
237+
application_probe_methods=self.APPLICATION_PROBE_METHODS,
238+
)
234239

235240
firmware_install_required = self._probed_firmware_info is None or (
236241
self._probed_firmware_info.firmware_type != expected_installed_firmware_type
@@ -295,6 +300,7 @@ async def _install_firmware(
295300
fw_data=fw_data,
296301
expected_installed_firmware_type=expected_installed_firmware_type,
297302
bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS,
303+
application_probe_methods=self.APPLICATION_PROBE_METHODS,
298304
progress_callback=lambda offset, total: self.async_update_progress(
299305
offset / total
300306
),

homeassistant/components/homeassistant_hardware/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware",
88
"integration_type": "system",
99
"requirements": [
10-
"universal-silabs-flasher==0.0.37",
10+
"universal-silabs-flasher==0.1.0",
1111
"ha-silabs-firmware-client==0.3.0"
1212
]
1313
}

homeassistant/components/homeassistant_hardware/update.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ class BaseFirmwareUpdateEntity(
8686

8787
# Subclasses provide the mapping between firmware types and entity descriptions
8888
entity_description: FirmwareUpdateEntityDescription
89-
bootloader_reset_methods: list[ResetTarget] = []
89+
BOOTLOADER_RESET_METHODS: list[ResetTarget]
90+
APPLICATION_PROBE_METHODS: list[tuple[ApplicationType, int]]
9091

9192
_attr_supported_features = (
9293
UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
@@ -278,7 +279,8 @@ async def async_install(
278279
device=self._current_device,
279280
fw_data=fw_data,
280281
expected_installed_firmware_type=self.entity_description.expected_firmware_type,
281-
bootloader_reset_methods=self.bootloader_reset_methods,
282+
bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS,
283+
application_probe_methods=self.APPLICATION_PROBE_METHODS,
282284
progress_callback=self._update_progress,
283285
domain=self._config_entry.domain,
284286
)

homeassistant/components/homeassistant_hardware/util.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import asyncio
66
from collections import defaultdict
7-
from collections.abc import AsyncIterator, Callable, Iterable, Sequence
7+
from collections.abc import AsyncIterator, Callable, Sequence
88
from contextlib import AsyncExitStack, asynccontextmanager
99
from dataclasses import dataclass
1010
from enum import StrEnum
@@ -309,15 +309,20 @@ async def guess_firmware_info(hass: HomeAssistant, device_path: str) -> Firmware
309309

310310

311311
async def probe_silabs_firmware_info(
312-
device: str, *, probe_methods: Iterable[ApplicationType] | None = None
312+
device: str,
313+
*,
314+
bootloader_reset_methods: Sequence[ResetTarget],
315+
application_probe_methods: Sequence[tuple[ApplicationType, int]],
313316
) -> FirmwareInfo | None:
314317
"""Probe the running firmware on a SiLabs device."""
315318
flasher = Flasher(
316319
device=device,
317-
**(
318-
{"probe_methods": [m.as_flasher_application_type() for m in probe_methods]}
319-
if probe_methods
320-
else {}
320+
probe_methods=tuple(
321+
(m.as_flasher_application_type(), baudrate)
322+
for m, baudrate in application_probe_methods
323+
),
324+
bootloader_reset=tuple(
325+
m.as_flasher_reset_target() for m in bootloader_reset_methods
321326
),
322327
)
323328

@@ -343,11 +348,18 @@ async def probe_silabs_firmware_info(
343348

344349

345350
async def probe_silabs_firmware_type(
346-
device: str, *, probe_methods: Iterable[ApplicationType] | None = None
351+
device: str,
352+
*,
353+
bootloader_reset_methods: Sequence[ResetTarget],
354+
application_probe_methods: Sequence[tuple[ApplicationType, int]],
347355
) -> ApplicationType | None:
348356
"""Probe the running firmware type on a SiLabs device."""
349357

350-
fw_info = await probe_silabs_firmware_info(device, probe_methods=probe_methods)
358+
fw_info = await probe_silabs_firmware_info(
359+
device,
360+
bootloader_reset_methods=bootloader_reset_methods,
361+
application_probe_methods=application_probe_methods,
362+
)
351363
if fw_info is None:
352364
return None
353365

@@ -359,12 +371,22 @@ async def async_flash_silabs_firmware(
359371
device: str,
360372
fw_data: bytes,
361373
expected_installed_firmware_type: ApplicationType,
362-
bootloader_reset_methods: Sequence[ResetTarget] = (),
374+
bootloader_reset_methods: Sequence[ResetTarget],
375+
application_probe_methods: Sequence[tuple[ApplicationType, int]],
363376
progress_callback: Callable[[int, int], None] | None = None,
364377
*,
365378
domain: str = DOMAIN,
366379
) -> FirmwareInfo:
367380
"""Flash firmware to the SiLabs device."""
381+
if not any(
382+
method == expected_installed_firmware_type
383+
for method, _ in application_probe_methods
384+
):
385+
raise ValueError(
386+
f"Expected installed firmware type {expected_installed_firmware_type!r}"
387+
f" not in application probe methods {application_probe_methods!r}"
388+
)
389+
368390
async with async_firmware_update_context(hass, device, domain):
369391
firmware_info = await guess_firmware_info(hass, device)
370392
_LOGGER.debug("Identified firmware info: %s", firmware_info)
@@ -373,11 +395,9 @@ async def async_flash_silabs_firmware(
373395

374396
flasher = Flasher(
375397
device=device,
376-
probe_methods=(
377-
ApplicationType.GECKO_BOOTLOADER.as_flasher_application_type(),
378-
ApplicationType.EZSP.as_flasher_application_type(),
379-
ApplicationType.SPINEL.as_flasher_application_type(),
380-
ApplicationType.CPC.as_flasher_application_type(),
398+
probe_methods=tuple(
399+
(m.as_flasher_application_type(), baudrate)
400+
for m, baudrate in application_probe_methods
381401
),
382402
bootloader_reset=tuple(
383403
m.as_flasher_reset_target() for m in bootloader_reset_methods
@@ -401,7 +421,13 @@ async def async_flash_silabs_firmware(
401421

402422
probed_firmware_info = await probe_silabs_firmware_info(
403423
device,
404-
probe_methods=(expected_installed_firmware_type,),
424+
bootloader_reset_methods=bootloader_reset_methods,
425+
# Only probe for the expected installed firmware type
426+
application_probe_methods=[
427+
(method, baudrate)
428+
for method, baudrate in application_probe_methods
429+
if method == expected_installed_firmware_type
430+
],
405431
)
406432

407433
if probed_firmware_info is None:

homeassistant/components/homeassistant_sky_connect/config_flow.py

Lines changed: 15 additions & 0 deletions
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.usb import (
2122
usb_service_info_from_device,
@@ -79,6 +80,20 @@ class SkyConnectFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
7980

8081
context: ConfigFlowContext
8182

83+
ZIGBEE_BAUDRATE = 115200
84+
# There is no hardware bootloader trigger
85+
BOOTLOADER_RESET_METHODS: list[ResetTarget] = []
86+
APPLICATION_PROBE_METHODS = [
87+
(ApplicationType.GECKO_BOOTLOADER, 115200),
88+
(ApplicationType.EZSP, ZIGBEE_BAUDRATE),
89+
(ApplicationType.SPINEL, 460800),
90+
# CPC baudrates can be removed once multiprotocol is removed
91+
(ApplicationType.CPC, 115200),
92+
(ApplicationType.CPC, 230400),
93+
(ApplicationType.CPC, 460800),
94+
(ApplicationType.ROUTER, 115200),
95+
]
96+
8297
def _get_translation_placeholders(self) -> dict[str, str]:
8398
"""Shared translation placeholders."""
8499
placeholders = {

homeassistant/components/homeassistant_sky_connect/update.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
2424

2525
from . import HomeAssistantSkyConnectConfigEntry
26+
from .config_flow import SkyConnectFirmwareMixin
2627
from .const import (
2728
DOMAIN,
2829
FIRMWARE,
@@ -151,8 +152,8 @@ async def async_setup_entry(
151152
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
152153
"""SkyConnect firmware update entity."""
153154

154-
# The ZBT-1 does not have a hardware bootloader trigger
155-
bootloader_reset_methods = []
155+
BOOTLOADER_RESET_METHODS = SkyConnectFirmwareMixin.BOOTLOADER_RESET_METHODS
156+
APPLICATION_PROBE_METHODS = SkyConnectFirmwareMixin.APPLICATION_PROBE_METHODS
156157

157158
def __init__(
158159
self,

homeassistant/components/homeassistant_yellow/config_flow.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,18 @@ async def _install_firmware_step(
8282
class YellowFirmwareMixin(ConfigEntryBaseFlow, FirmwareInstallFlowProtocol):
8383
"""Mixin for Home Assistant Yellow firmware methods."""
8484

85+
ZIGBEE_BAUDRATE = 115200
8586
BOOTLOADER_RESET_METHODS = [ResetTarget.YELLOW]
87+
APPLICATION_PROBE_METHODS = [
88+
(ApplicationType.GECKO_BOOTLOADER, 115200),
89+
(ApplicationType.EZSP, ZIGBEE_BAUDRATE),
90+
(ApplicationType.SPINEL, 460800),
91+
# CPC baudrates can be removed once multiprotocol is removed
92+
(ApplicationType.CPC, 115200),
93+
(ApplicationType.CPC, 230400),
94+
(ApplicationType.CPC, 460800),
95+
(ApplicationType.ROUTER, 115200),
96+
]
8697

8798
async def async_step_install_zigbee_firmware(
8899
self, user_input: dict[str, Any] | None = None
@@ -146,7 +157,11 @@ async def async_step_system(
146157
assert self._device is not None
147158

148159
# We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this
149-
self._probed_firmware_info = await probe_silabs_firmware_info(self._device)
160+
self._probed_firmware_info = await probe_silabs_firmware_info(
161+
self._device,
162+
bootloader_reset_methods=self.BOOTLOADER_RESET_METHODS,
163+
application_probe_methods=self.APPLICATION_PROBE_METHODS,
164+
)
150165

151166
# Kick off ZHA hardware discovery automatically if Zigbee firmware is running
152167
if (

homeassistant/components/homeassistant_yellow/update.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from homeassistant.components.homeassistant_hardware.util import (
1515
ApplicationType,
1616
FirmwareInfo,
17-
ResetTarget,
1817
)
1918
from homeassistant.components.update import UpdateDeviceClass
2019
from homeassistant.const import EntityCategory
@@ -24,6 +23,7 @@
2423
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
2524

2625
from . import HomeAssistantYellowConfigEntry
26+
from .config_flow import YellowFirmwareMixin
2727
from .const import DOMAIN, FIRMWARE, FIRMWARE_VERSION, MANUFACTURER, MODEL, RADIO_DEVICE
2828

2929
_LOGGER = logging.getLogger(__name__)
@@ -150,7 +150,8 @@ async def async_setup_entry(
150150
class FirmwareUpdateEntity(BaseFirmwareUpdateEntity):
151151
"""Yellow firmware update entity."""
152152

153-
bootloader_reset_methods = [ResetTarget.YELLOW] # Triggers a GPIO reset
153+
BOOTLOADER_RESET_METHODS = YellowFirmwareMixin.BOOTLOADER_RESET_METHODS
154+
APPLICATION_PROBE_METHODS = YellowFirmwareMixin.APPLICATION_PROBE_METHODS
154155

155156
def __init__(
156157
self,

0 commit comments

Comments
 (0)