Skip to content

Commit 125b537

Browse files
authored
OTA index file generation (#28)
* Create the `ota generate-index` command * Move the root logger object into its own variable * Document in README
1 parent de9b462 commit 125b537

File tree

4 files changed

+83
-3
lines changed

4 files changed

+83
-3
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ repos:
55
- id: black
66
args: ["--safe", "--quiet"]
77
- repo: https://github.com/charliermarsh/ruff-pre-commit
8-
rev: 'v0.0.244'
8+
rev: 'v0.0.246'
99
hooks:
1010
- id: ruff
1111
args: ["--fix"]

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,25 @@ $ zigpy ota dump-firmware 10047227-1.2-TRADFRI-cv-cct-unified-2.3.050.ota.ota.si
155155
Ember Version: 6.3.1.1
156156
```
157157

158+
## Generate OTA index files
159+
160+
Create a JSON index for a given directory of firmwares:
161+
162+
```console
163+
$ zigpy ota generate-index --ota-url-root="https://example.org/fw" path/to/firmwares/**/*.ota
164+
2023-02-14 12:02:03.532 ubuntu zigpy_cli.ota INFO Parsing path/to/firmwares/fw/test.ota
165+
2023-02-14 12:02:03.533 ubuntu zigpy_cli.ota INFO Writing path/to/firmwares/fw/test.ota
166+
[
167+
{
168+
"binary_url": "https://example.org/fw/test.ota",
169+
"file_version": 1762356,
170+
"image_type": 1234,
171+
"manufacturer_id": 5678,
172+
"changelog": "",
173+
"checksum": "sha3-256:1ddaa649eb920dea9e5f002fe0d1443cc903ac0c1b26e7ad2c97b928edec2786"
174+
},
175+
...
176+
```
158177

159178
# PCAP
160179
## Re-calculate the FCS on a packet capture

zigpy_cli/cli.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from zigpy_cli.const import LOG_LEVELS
1111

1212
LOGGER = logging.getLogger(__name__)
13+
ROOT_LOGGER = logging.getLogger()
1314

1415

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

3334
LOGGER.setLevel(log_level)
34-
logging.getLogger().setLevel(log_level)
35+
ROOT_LOGGER.setLevel(log_level)
3536

3637
coloredlogs.install(
3738
fmt=(
@@ -42,5 +43,5 @@ def cli(verbose):
4243
),
4344
level=log_level,
4445
level_styles=level_styles,
45-
logger=logging.getLogger(),
46+
logger=ROOT_LOGGER,
4647
)

zigpy_cli/ota.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import hashlib
4+
import json
35
import logging
46
import pathlib
57

@@ -65,3 +67,61 @@ def dump_firmware(input, output):
6567
break
6668
else:
6769
LOGGER.warning("Image has no UPGRADE_IMAGE subelements")
70+
71+
72+
@ota.command()
73+
@click.pass_context
74+
@click.option("--ota-url-root", type=str, default=None)
75+
@click.option("--output", type=click.File("w"), default="-")
76+
@click.argument("files", nargs=-1, type=pathlib.Path)
77+
def generate_index(ctx, ota_url_root, output, files):
78+
if ctx.parent.parent.params["verbose"] == 0:
79+
cli.callback(verbose=1)
80+
81+
ota_metadata = []
82+
83+
for f in files:
84+
if not f.is_file():
85+
continue
86+
87+
LOGGER.info("Parsing %s", f)
88+
contents = f.read_bytes()
89+
90+
try:
91+
image, rest = parse_ota_image(contents)
92+
except Exception as e:
93+
LOGGER.error("Failed to parse: %s", e)
94+
continue
95+
96+
if rest:
97+
LOGGER.error("Image has trailing data: %r", rest)
98+
continue
99+
100+
try:
101+
validate_ota_image(image)
102+
except Exception as e:
103+
LOGGER.error("Image is invalid: %s", e)
104+
105+
if ota_url_root is not None:
106+
url = f"{ota_url_root.rstrip('/')}/{f.name}"
107+
else:
108+
url = None
109+
110+
metadata = {
111+
"binary_url": url,
112+
"file_version": image.header.file_version,
113+
"image_type": image.header.image_type,
114+
"manufacturer_id": image.header.manufacturer_id,
115+
"changelog": "",
116+
"checksum": f"sha3-256:{hashlib.sha3_256(contents).hexdigest()}",
117+
}
118+
119+
if image.header.hardware_versions_present:
120+
metadata["min_hardware_version"] = image.header.minimum_hardware_version
121+
metadata["max_hardware_version"] = image.header.maximum_hardware_version
122+
123+
LOGGER.info("Writing %s", f)
124+
ota_metadata.append(metadata)
125+
126+
json.dump(ota_metadata, output, indent=4)
127+
output.write("\n")

0 commit comments

Comments
 (0)