Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
- id: black
args: ["--safe", "--quiet"]
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.244'
rev: 'v0.0.246'
hooks:
- id: ruff
args: ["--fix"]
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,25 @@ $ zigpy ota dump-firmware 10047227-1.2-TRADFRI-cv-cct-unified-2.3.050.ota.ota.si
Ember Version: 6.3.1.1
```

## Generate OTA index files

Create a JSON index for a given directory of firmwares:

```console
$ zigpy ota generate-index --ota-url-root="https://example.org/fw" path/to/firmwares/**/*.ota
2023-02-14 12:02:03.532 ubuntu zigpy_cli.ota INFO Parsing path/to/firmwares/fw/test.ota
2023-02-14 12:02:03.533 ubuntu zigpy_cli.ota INFO Writing path/to/firmwares/fw/test.ota
[
{
"binary_url": "https://example.org/fw/test.ota",
"file_version": 1762356,
"image_type": 1234,
"manufacturer_id": 5678,
"changelog": "",
"checksum": "sha3-256:1ddaa649eb920dea9e5f002fe0d1443cc903ac0c1b26e7ad2c97b928edec2786"
},
...
```

# PCAP
## Re-calculate the FCS on a packet capture
Expand Down
5 changes: 3 additions & 2 deletions zigpy_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from zigpy_cli.const import LOG_LEVELS

LOGGER = logging.getLogger(__name__)
ROOT_LOGGER = logging.getLogger()


def click_coroutine(cmd):
Expand All @@ -31,7 +32,7 @@ def cli(verbose):
level_styles["trace"] = level_styles["spam"]

LOGGER.setLevel(log_level)
logging.getLogger().setLevel(log_level)
ROOT_LOGGER.setLevel(log_level)

coloredlogs.install(
fmt=(
Expand All @@ -42,5 +43,5 @@ def cli(verbose):
),
level=log_level,
level_styles=level_styles,
logger=logging.getLogger(),
logger=ROOT_LOGGER,
)
60 changes: 60 additions & 0 deletions zigpy_cli/ota.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import hashlib
import json
import logging
import pathlib

Expand Down Expand Up @@ -65,3 +67,61 @@ def dump_firmware(input, output):
break
else:
LOGGER.warning("Image has no UPGRADE_IMAGE subelements")


@ota.command()
@click.pass_context
@click.option("--ota-url-root", type=str, default=None)
@click.option("--output", type=click.File("w"), default="-")
@click.argument("files", nargs=-1, type=pathlib.Path)
def generate_index(ctx, ota_url_root, output, files):
if ctx.parent.parent.params["verbose"] == 0:
cli.callback(verbose=1)

ota_metadata = []

for f in files:
if not f.is_file():
continue

LOGGER.info("Parsing %s", f)
contents = f.read_bytes()

try:
image, rest = parse_ota_image(contents)
except Exception as e:
LOGGER.error("Failed to parse: %s", e)
continue

if rest:
LOGGER.error("Image has trailing data: %r", rest)
continue

try:
validate_ota_image(image)
except Exception as e:
LOGGER.error("Image is invalid: %s", e)

if ota_url_root is not None:
url = f"{ota_url_root.rstrip('/')}/{f.name}"
else:
url = None

metadata = {
"binary_url": url,
"file_version": image.header.file_version,
"image_type": image.header.image_type,
"manufacturer_id": image.header.manufacturer_id,
"changelog": "",
"checksum": f"sha3-256:{hashlib.sha3_256(contents).hexdigest()}",
}

if image.header.hardware_versions_present:
metadata["min_hardware_version"] = image.header.minimum_hardware_version
metadata["max_hardware_version"] = image.header.maximum_hardware_version

LOGGER.info("Writing %s", f)
ota_metadata.append(metadata)

json.dump(ota_metadata, output, indent=4)
output.write("\n")