Skip to content

Commit

Permalink
Read CPC firmware secondary version (NabuCasa#62)
Browse files Browse the repository at this point in the history
* Add Z-Wave and Gecko bootloader firmware types

* Handle secondary CPC version

* Make `--ensure-exact-version` works exactly

* Fix unit test
  • Loading branch information
puddly authored Jan 17, 2024
1 parent c61780b commit 67a0232
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 9 deletions.
1 change: 1 addition & 0 deletions tests/test_firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def test_firmware_gbl_valid_with_metadata():
metadata_version=1,
sdk_version=Version("4.1.3"),
ezsp_version=None,
cpc_version=None,
fw_type=firmware.FirmwareImageType.RCP_UART_802154,
ot_rcp_version=None,
baudrate=None,
Expand Down
1 change: 1 addition & 0 deletions universal_silabs_flasher/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ def __init__(self, version: str) -> None:
# 7.2.2.0 build 190
# 4.2.2
# SL-OPENTHREAD/2.2.2.0_GitHub-91fa1f455
# 4.4.0-2546d625-dirty-676fdb09
for component in self._SEPARATORS_REGEX.split(version):
if component.isdigit():
self.components.append(
Expand Down
7 changes: 7 additions & 0 deletions universal_silabs_flasher/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ class FirmwareImageType(enum.Enum):
# OpenThread RCP
OT_RCP = "ot-rcp"

# Z-Wave
Z_WAVE = "z-wave"

# Gecko Bootloader
GECKO_BOOTLOADER = "gecko-bootloader"


class ApplicationType(enum.Enum):
GECKO_BOOTLOADER = "bootloader"
Expand All @@ -27,6 +33,7 @@ class ApplicationType(enum.Enum):
FirmwareImageType.RCP_UART_802154: ApplicationType.CPC,
FirmwareImageType.ZIGBEE_NCP_RCP_UART_802154: ApplicationType.CPC,
FirmwareImageType.OT_RCP: ApplicationType.SPINEL,
FirmwareImageType.GECKO_BOOTLOADER: ApplicationType.GECKO_BOOTLOADER,
}


Expand Down
14 changes: 12 additions & 2 deletions universal_silabs_flasher/cpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,14 @@ def __init__(self) -> None:
self._pending_frames: dict[int, asyncio.Future] = {}

async def probe(self) -> Version:
return await self.get_cpc_version()
cpc_version = await self.get_cpc_version()
secondary_version = await self.get_secondary_version()

# Prefer the secondary version if possible, on newer firmwares we customize it
if secondary_version is not None:
return secondary_version

return cpc_version

async def enter_bootloader(self) -> None:
"""Reboot into the bootloader."""
Expand Down Expand Up @@ -254,7 +261,7 @@ async def get_cpc_version(self) -> Version:

return Version(f"{major}.{minor}.{patch}")

async def get_secondary_version(self) -> Version:
async def get_secondary_version(self) -> Version | None:
"""Read the secondary app version from the device."""
rsp = await self.send_unnumbered_frame(
command_id=cpc_types.UnnumberedFrameCommandId.PROP_VALUE_GET,
Expand All @@ -267,6 +274,9 @@ async def get_secondary_version(self) -> Version:

version_bytes = rsp.payload.payload.value

if version_bytes == b"UNDEFINED\x00":
return None

return Version(version_bytes.split(b"\x00", 1)[0].decode("ascii"))

def data_received(self, data: bytes) -> None:
Expand Down
12 changes: 11 additions & 1 deletion universal_silabs_flasher/firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,20 @@ class NabuCasaMetadata:
sdk_version: Version | None
ezsp_version: Version | None
ot_rcp_version: Version | None
cpc_version: Version | None

fw_type: FirmwareImageType | None
baudrate: int | None

original_json: dict[str, typing.Any] = dataclasses.field(repr=False)

def get_public_version(self) -> Version | None:
return self.ezsp_version or self.ot_rcp_version or self.sdk_version
return (
self.cpc_version
or self.ezsp_version
or self.ot_rcp_version
or self.sdk_version
)

@classmethod
def from_json(cls, obj: dict[str, typing.Any]) -> NabuCasaMetadata:
Expand All @@ -98,6 +104,9 @@ def from_json(cls, obj: dict[str, typing.Any]) -> NabuCasaMetadata:
if ot_rcp_version := obj.pop("ot_rcp_version", None):
ot_rcp_version = Version(ot_rcp_version)

if cpc_version := obj.pop("cpc_version", None):
cpc_version = Version(cpc_version)

if fw_type := obj.pop("fw_type", None):
fw_type = FirmwareImageType(fw_type)

Expand All @@ -111,6 +120,7 @@ def from_json(cls, obj: dict[str, typing.Any]) -> NabuCasaMetadata:
sdk_version=sdk_version,
ezsp_version=ezsp_version,
ot_rcp_version=ot_rcp_version,
cpc_version=cpc_version,
fw_type=fw_type,
baudrate=baudrate,
original_json=original_json,
Expand Down
12 changes: 6 additions & 6 deletions universal_silabs_flasher/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,17 @@ async def flash(
flasher.app_baudrate,
metadata.baudrate,
)
elif app_version.compatible_with(fw_version):
_LOGGER.info(
"Firmware version %s is flashed, not re-installing", app_version
)
return
elif ensure_exact_version and not app_version.compatible_with(fw_version):
elif ensure_exact_version and app_version != fw_version:
_LOGGER.info(
"Firmware version %s does not match expected version %s",
fw_version,
app_version,
)
elif app_version.compatible_with(fw_version):
_LOGGER.info(
"Firmware version %s is flashed, not re-installing", app_version
)
return
elif not allow_downgrades and app_version > fw_version:
_LOGGER.info(
"Firmware version %s does not upgrade current version %s",
Expand Down

0 comments on commit 67a0232

Please sign in to comment.