Skip to content

Commit

Permalink
Rename firmware types and support variant metadata (#81)
Browse files Browse the repository at this point in the history
* Use the new firmware type names, with remappings

* Implement `variant`

* Bump metadata version

* Add a unit test for the new metadata format

* Gracefully handle bad metadata

* Drop default Python version from CI
  • Loading branch information
puddly authored Oct 16, 2024
1 parent 5cee8a2 commit c7d52eb
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 38 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ jobs:
with:
CODE_FOLDER: universal_silabs_flasher
CACHE_VERSION: 2
PYTHON_VERSION_DEFAULT: 3.8.14
PRE_COMMIT_CACHE_PATH: ~/.cache/pre-commit
MINIMUM_COVERAGE_PERCENTAGE: 40
secrets:
Expand Down
Binary file not shown.
43 changes: 35 additions & 8 deletions tests/test_firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ def test_firmware_ebl_valid():
fw.get_nabucasa_metadata()


def test_firmware_gbl_valid_no_metadata():
data = (
FIRMWARES_DIR / "NabuCasa_EZSP_v6.10.3.0_PB32_ncp-uart-hw_115200.gbl"
).read_bytes()
fw = firmware.parse_firmware_image(data)

assert isinstance(fw, firmware.GBLImage)
assert fw.serialize() == data

with pytest.raises(KeyError):
fw.get_nabucasa_metadata()


def test_firmware_gbl_valid_with_metadata():
data = (
FIRMWARES_DIR / "NabuCasa_SkyConnect_RCP_v4.1.3_rcp-uart-hw-802154_115200.gbl"
Expand All @@ -32,7 +45,8 @@ def test_firmware_gbl_valid_with_metadata():
sdk_version=Version("4.1.3"),
ezsp_version=None,
cpc_version=None,
fw_type=firmware.FirmwareImageType.RCP_UART_802154,
fw_type=firmware.FirmwareImageType.MULTIPAN,
fw_variant=None,
ot_rcp_version=None,
baudrate=None,
original_json={
Expand All @@ -43,14 +57,27 @@ def test_firmware_gbl_valid_with_metadata():
)


def test_firmware_gbl_valid_no_metadata():
data = (
FIRMWARES_DIR / "NabuCasa_EZSP_v6.10.3.0_PB32_ncp-uart-hw_115200.gbl"
).read_bytes()
def test_firmware_gbl_valid_with_metadata_v2():
data = (FIRMWARES_DIR / "skyconnect_zigbee_ncp_7.4.4.0.gbl").read_bytes()
fw = firmware.parse_firmware_image(data)

assert isinstance(fw, firmware.GBLImage)
assert fw.serialize() == data

with pytest.raises(KeyError):
fw.get_nabucasa_metadata()
assert fw.get_nabucasa_metadata() == firmware.NabuCasaMetadata(
metadata_version=2,
sdk_version=Version("4.4.4"),
ezsp_version=Version("7.4.4.0"),
cpc_version=None,
fw_type=firmware.FirmwareImageType.ZIGBEE_NCP,
fw_variant=None,
ot_rcp_version=None,
baudrate=115200,
original_json={
"baudrate": 115200,
"ezsp_version": "7.4.4.0",
"fw_type": "zigbee_ncp",
"fw_variant": None,
"metadata_version": 2,
"sdk_version": "4.4.4",
},
)
43 changes: 21 additions & 22 deletions universal_silabs_flasher/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@


class FirmwareImageType(enum.Enum):
# EmberZNet Zigbee firmware
NCP_UART_HW = "ncp-uart-hw"

# Multi-PAN RCP Multiprotocol (via zigbeed)
RCP_UART_802154 = "rcp-uart-802154"

# Zigbee NCP + OpenThread RCP
ZIGBEE_NCP_RCP_UART_802154 = "zigbee-ncp-rcp-uart-802154"

# OpenThread RCP
OT_RCP = "ot-rcp"

# Z-Wave
Z_WAVE = "z-wave"

# Gecko Bootloader
GECKO_BOOTLOADER = "gecko-bootloader"
ZIGBEE_NCP = "zigbee_ncp"
OPENTHREAD_RCP = "openthread_rcp"
ZWAVE_NCP = "zwave_ncp"
BOOTLOADER = "bootloader"
MULTIPAN = "multipan"

UNKNOWN = "unknown"


LEGACY_FIRMWARE_TYPE_REMAPPING = {
"ncp-uart-hw": FirmwareImageType.ZIGBEE_NCP,
"ncp-uart-sw": FirmwareImageType.ZIGBEE_NCP,
"rcp-uart-802154": FirmwareImageType.MULTIPAN,
"ot-rcp": FirmwareImageType.OPENTHREAD_RCP,
"z-wave": FirmwareImageType.ZWAVE_NCP,
"gecko-bootloader": FirmwareImageType.BOOTLOADER,
}


class ApplicationType(enum.Enum):
Expand All @@ -29,11 +29,10 @@ class ApplicationType(enum.Enum):


FW_IMAGE_TYPE_TO_APPLICATION_TYPE = {
FirmwareImageType.NCP_UART_HW: ApplicationType.EZSP,
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,
FirmwareImageType.ZIGBEE_NCP: ApplicationType.EZSP,
FirmwareImageType.MULTIPAN: ApplicationType.CPC,
FirmwareImageType.OPENTHREAD_RCP: ApplicationType.SPINEL,
FirmwareImageType.BOOTLOADER: ApplicationType.GECKO_BOOTLOADER,
}


Expand Down
18 changes: 15 additions & 3 deletions universal_silabs_flasher/firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
import zigpy.types as zigpy_t

from .common import Version, pad_to_multiple
from .const import FirmwareImageType
from .const import LEGACY_FIRMWARE_TYPE_REMAPPING, FirmwareImageType

_LOGGER = logging.getLogger(__name__)

NABUCASA_METADATA_VERSION = 1
NABUCASA_METADATA_VERSION = 2


class GBLTagId(zigpy_t.enum32):
Expand Down Expand Up @@ -72,6 +72,7 @@ class NabuCasaMetadata:
cpc_version: Version | None

fw_type: FirmwareImageType | None
fw_variant: str | None
baudrate: int | None

original_json: dict[str, typing.Any] = dataclasses.field(repr=False)
Expand Down Expand Up @@ -108,7 +109,17 @@ def from_json(cls, obj: dict[str, typing.Any]) -> NabuCasaMetadata:
cpc_version = Version(cpc_version)

if fw_type := obj.pop("fw_type", None):
fw_type = FirmwareImageType(fw_type)
if fw_type in LEGACY_FIRMWARE_TYPE_REMAPPING:
fw_type = LEGACY_FIRMWARE_TYPE_REMAPPING[fw_type]

try:
fw_type = FirmwareImageType(fw_type)
except ValueError:
_LOGGER.warning("Unknown firmware type: %r", fw_type)
fw_type = None

if fw_variant := obj.pop("fw_variant", None):
fw_variant = fw_variant

baudrate = obj.pop("baudrate", None)

Expand All @@ -122,6 +133,7 @@ def from_json(cls, obj: dict[str, typing.Any]) -> NabuCasaMetadata:
ot_rcp_version=ot_rcp_version,
cpc_version=cpc_version,
fw_type=fw_type,
fw_variant=fw_variant,
baudrate=baudrate,
original_json=original_json,
)
Expand Down
9 changes: 5 additions & 4 deletions universal_silabs_flasher/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ async def flash(

try:
metadata = fw_image.get_nabucasa_metadata()
except KeyError:
except Exception:
_LOGGER.info("Failed to read firmware metadata: {exc!r}")
metadata = None
else:
_LOGGER.info("Extracted GBL metadata: %s", metadata)
Expand Down Expand Up @@ -343,12 +344,12 @@ async def flash(
raise click.ClickException(str(e)) from e

if flasher.app_type == ApplicationType.EZSP:
running_image_type = FirmwareImageType.NCP_UART_HW
running_image_type = FirmwareImageType.ZIGBEE_NCP
elif flasher.app_type == ApplicationType.SPINEL:
running_image_type = FirmwareImageType.OT_RCP
running_image_type = FirmwareImageType.OPENTHREAD_RCP
elif flasher.app_type == ApplicationType.CPC:
# TODO: how do you distinguish RCP_UART_802154 from ZIGBEE_NCP_RCP_UART_802154?
running_image_type = FirmwareImageType.RCP_UART_802154
running_image_type = FirmwareImageType.MULTIPAN
elif flasher.app_type == ApplicationType.GECKO_BOOTLOADER:
running_image_type = None
else:
Expand Down

0 comments on commit c7d52eb

Please sign in to comment.